Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4bd2ca3f0d | ||
|
e63157cc33 | ||
|
7f2fb049f5 | ||
|
4744bde0e5 | ||
|
4a835bb2b9 | ||
|
ddc9ae2a83 | ||
|
d6d3cdd573 |
@@ -44,7 +44,7 @@
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="7.0.2" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
|
@@ -189,7 +189,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
if (start.CompareTo(node.End) < 0)
|
||||
{
|
||||
if (overlaps.Length >= overlapCount)
|
||||
if (overlaps.Length <= overlapCount)
|
||||
{
|
||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||
}
|
||||
|
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
"Light" => ThemeVariant.Light,
|
||||
"Dark" => ThemeVariant.Dark,
|
||||
_ => ThemeVariant.Default
|
||||
_ => ThemeVariant.Default,
|
||||
};
|
||||
|
||||
if (enableCustomTheme)
|
||||
|
@@ -3,7 +3,6 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Rendering;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
|
@@ -145,7 +145,7 @@ namespace Ryujinx.Ava.Common
|
||||
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
||||
AllowMultiple = false
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (result.Count == 0)
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
|
@@ -82,12 +82,9 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
|
||||
_running = false;
|
||||
|
||||
@@ -114,10 +111,9 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||
"");
|
||||
}
|
||||
|
||||
_running = false;
|
||||
@@ -134,10 +130,9 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||
"");
|
||||
}
|
||||
|
||||
_running = false;
|
||||
@@ -149,10 +144,8 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||
});
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||
|
||||
_running = false;
|
||||
|
||||
@@ -167,12 +160,9 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
|
||||
_running = false;
|
||||
|
||||
@@ -183,10 +173,9 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||
"");
|
||||
}
|
||||
|
||||
_running = false;
|
||||
@@ -212,7 +201,7 @@ namespace Ryujinx.Modules
|
||||
_buildSize = -1;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
// Show a message asking the user if they want to update
|
||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
||||
@@ -222,7 +211,7 @@ namespace Ryujinx.Modules
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
UpdateRyujinx(mainWindow, _buildUrl);
|
||||
await UpdateRyujinx(mainWindow, _buildUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -241,7 +230,7 @@ namespace Ryujinx.Modules
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async void UpdateRyujinx(Window parent, string downloadUrl)
|
||||
private static async Task UpdateRyujinx(Window parent, string downloadUrl)
|
||||
{
|
||||
_updateSuccessful = false;
|
||||
|
||||
@@ -579,27 +568,24 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||
{
|
||||
// Extract Update
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
|
||||
await Task.Run(() =>
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
});
|
||||
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
// Delete downloaded zip
|
||||
File.Delete(updateFile);
|
||||
@@ -613,36 +599,33 @@ namespace Ryujinx.Modules
|
||||
if (!OperatingSystem.IsMacOS())
|
||||
{
|
||||
// Replace old files
|
||||
await Task.Run(() =>
|
||||
double count = 0;
|
||||
foreach (string file in allFiles)
|
||||
{
|
||||
double count = 0;
|
||||
foreach (string file in allFiles)
|
||||
count++;
|
||||
try
|
||||
{
|
||||
count++;
|
||||
try
|
||||
{
|
||||
File.Move(file, file + ".ryuold");
|
||||
File.Move(file, file + ".ryuold");
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
catch
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||
}
|
||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
catch
|
||||
{
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
});
|
||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||
}
|
||||
}
|
||||
|
||||
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
});
|
||||
|
||||
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
|
||||
|
||||
Directory.Delete(_updateDir, true);
|
||||
}
|
||||
|
||||
@@ -658,12 +641,11 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -673,12 +655,11 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -688,12 +669,11 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -705,21 +685,19 @@ namespace Ryujinx.Modules
|
||||
{
|
||||
if (ReleaseInformation.IsFlatHubBuild())
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -106,7 +106,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
bool error = false;
|
||||
string inputText = args.InitialText ?? "";
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -149,7 +149,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
bool showDetails = false;
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
|
||||
{
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Account, new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +62,14 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default);
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Device, default);
|
||||
}
|
||||
|
||||
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default);
|
||||
OpenSaveDirectory(viewModel, SaveDataType.Bcat, default);
|
||||
}
|
||||
|
||||
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
|
||||
@@ -158,11 +158,12 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
@@ -205,11 +206,12 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
@@ -335,13 +337,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||
await viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -212,9 +212,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
||||
}
|
||||
}
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
foreach (var file in result)
|
||||
|
@@ -26,6 +26,7 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
@@ -39,7 +40,6 @@ using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
||||
@@ -1068,9 +1068,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
||||
|
||||
static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
||||
|
||||
Dispatcher.UIThread.Post(Action);
|
||||
await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1163,16 +1161,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
AppHost?.DisposeContext();
|
||||
}
|
||||
|
||||
private void HandleRelaunch()
|
||||
private async Task HandleRelaunch()
|
||||
{
|
||||
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
|
||||
{
|
||||
UserChannelPersistence.ShouldRestart = false;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
LoadApplication(_currentEmulatedGamePath);
|
||||
});
|
||||
await LoadApplication(_currentEmulatedGamePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1191,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Application.Current.Styles.TryGetResource(args.VSyncEnabled
|
||||
? "VsyncEnabled"
|
||||
: "VsyncDisabled",
|
||||
Avalonia.Application.Current.ActualThemeVariant,
|
||||
Application.Current.ActualThemeVariant,
|
||||
out object color);
|
||||
|
||||
if (color is not null)
|
||||
@@ -1283,7 +1278,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Glyph = Glyph.Grid;
|
||||
}
|
||||
|
||||
public async void InstallFirmwareFromFile()
|
||||
public async Task InstallFirmwareFromFile()
|
||||
{
|
||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
@@ -1294,21 +1289,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Patterns = new[] { "*.xci", "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/x-nx-xci", "application/zip" }
|
||||
MimeTypes = new[] { "application/x-nx-xci", "application/zip" },
|
||||
},
|
||||
new("XCI")
|
||||
{
|
||||
Patterns = new[] { "*.xci" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||
MimeTypes = new[] { "application/x-nx-xci" }
|
||||
MimeTypes = new[] { "application/x-nx-xci" },
|
||||
},
|
||||
new("ZIP")
|
||||
{
|
||||
Patterns = new[] { "*.zip" },
|
||||
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
||||
MimeTypes = new[] { "application/zip" }
|
||||
MimeTypes = new[] { "application/zip" },
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
@@ -1317,11 +1312,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void InstallFirmwareFromFolder()
|
||||
public async Task InstallFirmwareFromFolder()
|
||||
{
|
||||
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
@@ -1352,7 +1347,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void ExitCurrentState()
|
||||
public async Task ExitCurrentState()
|
||||
{
|
||||
if (WindowState == WindowState.FullScreen)
|
||||
{
|
||||
@@ -1377,7 +1372,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async void ManageProfiles()
|
||||
public async Task ManageProfiles()
|
||||
{
|
||||
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
||||
}
|
||||
@@ -1387,7 +1382,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
AppHost.Device.System.SimulateWakeUpMessage();
|
||||
}
|
||||
|
||||
public async void OpenFile()
|
||||
public async Task OpenFile()
|
||||
{
|
||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
@@ -1404,7 +1399,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
"com.ryujinx.xci",
|
||||
"com.ryujinx.nca",
|
||||
"com.ryujinx.nro",
|
||||
"com.ryujinx.nso"
|
||||
"com.ryujinx.nso",
|
||||
},
|
||||
MimeTypes = new[]
|
||||
{
|
||||
@@ -1412,63 +1407,63 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
"application/x-nx-xci",
|
||||
"application/x-nx-nca",
|
||||
"application/x-nx-nro",
|
||||
"application/x-nx-nso"
|
||||
}
|
||||
"application/x-nx-nso",
|
||||
},
|
||||
},
|
||||
new("NSP")
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
},
|
||||
new("XCI")
|
||||
{
|
||||
Patterns = new[] { "*.xci" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||
MimeTypes = new[] { "application/x-nx-xci" }
|
||||
MimeTypes = new[] { "application/x-nx-xci" },
|
||||
},
|
||||
new("NCA")
|
||||
{
|
||||
Patterns = new[] { "*.nca" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
||||
MimeTypes = new[] { "application/x-nx-nca" }
|
||||
MimeTypes = new[] { "application/x-nx-nca" },
|
||||
},
|
||||
new("NRO")
|
||||
{
|
||||
Patterns = new[] { "*.nro" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
||||
MimeTypes = new[] { "application/x-nx-nro" }
|
||||
MimeTypes = new[] { "application/x-nx-nro" },
|
||||
},
|
||||
new("NSO")
|
||||
{
|
||||
Patterns = new[] { "*.nso" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
||||
MimeTypes = new[] { "application/x-nx-nso" }
|
||||
MimeTypes = new[] { "application/x-nx-nso" },
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
LoadApplication(result[0].Path.LocalPath);
|
||||
await LoadApplication(result[0].Path.LocalPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenFolder()
|
||||
public async Task OpenFolder()
|
||||
{
|
||||
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
|
||||
AllowMultiple = false
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
LoadApplication(result[0].Path.LocalPath);
|
||||
await LoadApplication(result[0].Path.LocalPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||
public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
@@ -1505,35 +1500,30 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
this,
|
||||
TopLevel);
|
||||
|
||||
async void Action()
|
||||
if (!await AppHost.LoadGuestApplication())
|
||||
{
|
||||
if (!await AppHost.LoadGuestApplication())
|
||||
{
|
||||
AppHost.DisposeContext();
|
||||
AppHost = null;
|
||||
AppHost.DisposeContext();
|
||||
AppHost = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CanUpdate = false;
|
||||
|
||||
LoadHeading = TitleName = titleName;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
{
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
}
|
||||
|
||||
SwitchToRenderer(startFullscreen);
|
||||
|
||||
_currentEmulatedGamePath = path;
|
||||
|
||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||
gameThread.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(Action);
|
||||
CanUpdate = false;
|
||||
|
||||
LoadHeading = TitleName = titleName;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
{
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
}
|
||||
|
||||
SwitchToRenderer(startFullscreen);
|
||||
|
||||
_currentEmulatedGamePath = path;
|
||||
|
||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||
gameThread.Start();
|
||||
}
|
||||
|
||||
public void SwitchToRenderer(bool startFullscreen)
|
||||
@@ -1596,7 +1586,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
IsGameRunning = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
ShowMenuAndStatusBar = true;
|
||||
ShowContent = true;
|
||||
@@ -1609,7 +1599,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
AppHost = null;
|
||||
|
||||
HandleRelaunch();
|
||||
await HandleRelaunch();
|
||||
});
|
||||
|
||||
RendererHostControl.WindowCreated -= RendererHost_Created;
|
||||
|
@@ -78,14 +78,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||
"",
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
|
||||
});
|
||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
|
||||
);
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
|
@@ -22,6 +22,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
|
||||
@@ -184,18 +185,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||
});
|
||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||
});
|
||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +202,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
SortUpdates();
|
||||
}
|
||||
|
||||
public async void Add()
|
||||
public async Task Add()
|
||||
{
|
||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
@@ -218,9 +213,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Patterns = new[] { "*.nsp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
||||
}
|
||||
}
|
||||
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
foreach (var file in result)
|
||||
|
@@ -17,7 +17,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
@@ -107,20 +106,14 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
private void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Pause();
|
||||
});
|
||||
Window.ViewModel.AppHost?.Pause();
|
||||
}
|
||||
|
||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
private void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Resume();
|
||||
});
|
||||
Window.ViewModel.AppHost?.Resume();
|
||||
}
|
||||
|
||||
public async void OpenSettings(object sender, RoutedEventArgs e)
|
||||
@@ -132,13 +125,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
public void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
{
|
||||
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||
await ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +189,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
if (FileAssociationHelper.Install())
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
|
||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -209,8 +201,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
if (FileAssociationHelper.Uninstall())
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
|
||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
Patterns = new[] { "*.xaml" },
|
||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
|
||||
MimeTypes = new[] { "application/xaml+xml" }
|
||||
}
|
||||
}
|
||||
MimeTypes = new[] { "application/xaml+xml" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
|
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
{
|
||||
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
||||
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
|
||||
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" }
|
||||
}
|
||||
}
|
||||
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result.Count > 0)
|
||||
|
@@ -25,6 +25,7 @@ using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
@@ -79,35 +80,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
|
||||
ViewModel.Initialize(
|
||||
ContentManager,
|
||||
StorageProvider,
|
||||
ApplicationLibrary,
|
||||
VirtualFileSystem,
|
||||
AccountManager,
|
||||
InputManager,
|
||||
_userChannelPersistence,
|
||||
LibHacHorizonManager,
|
||||
UiHandler,
|
||||
ShowLoading,
|
||||
SwitchToGameControl,
|
||||
SetMainContent,
|
||||
this);
|
||||
|
||||
ViewModel.RefreshFirmwareStatus();
|
||||
|
||||
LoadGameList();
|
||||
|
||||
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
||||
this.ScalingChanged += OnScalingChanged;
|
||||
}
|
||||
|
||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
@@ -122,36 +99,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.IsActive = obj;
|
||||
}
|
||||
|
||||
public void LoadGameList()
|
||||
{
|
||||
if (_isLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
|
||||
LoadApplications();
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
private void OnScalingChanged(object sender, EventArgs e)
|
||||
{
|
||||
Program.DesktopScaleFactor = this.RenderScaling;
|
||||
}
|
||||
|
||||
public void AddApplication(ApplicationData applicationData)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ViewModel.Applications.Add(applicationData);
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
||||
{
|
||||
AddApplication(e.AppData);
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ViewModel.Applications.Add(e.AppData);
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
|
||||
@@ -183,7 +141,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
string path = new FileInfo(args.Application.Path).FullName;
|
||||
|
||||
ViewModel.LoadApplication(path);
|
||||
ViewModel.LoadApplication(path).Wait();
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
@@ -202,13 +160,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.ShowContent = true;
|
||||
ViewModel.IsLoadingIndeterminate = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
});
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowLoading(bool startFullscreen = false)
|
||||
@@ -217,13 +172,10 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.ShowLoadProgress = true;
|
||||
ViewModel.IsLoadingIndeterminate = true;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
});
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
@@ -255,7 +207,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static async void ShowVmMaxMapCountWarning()
|
||||
private static async Task ShowVmMaxMapCountWarning()
|
||||
{
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
||||
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
||||
@@ -267,7 +219,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static async void ShowVmMaxMapCountDialog()
|
||||
private static async Task ShowVmMaxMapCountDialog()
|
||||
{
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
||||
LinuxHelper.RecommendedVmMaxMapCount);
|
||||
@@ -313,33 +265,46 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
private void CheckLaunchState()
|
||||
{
|
||||
if (ShowKeyErrorOnLoad)
|
||||
{
|
||||
ShowKeyErrorOnLoad = false;
|
||||
|
||||
Dispatcher.UIThread.Post(async () => await
|
||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
|
||||
|
||||
if (LinuxHelper.PkExecPath is not null)
|
||||
{
|
||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
await ShowVmMaxMapCountDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
await ShowVmMaxMapCountWarning();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (_deferLoad)
|
||||
if (!ShowKeyErrorOnLoad)
|
||||
{
|
||||
_deferLoad = false;
|
||||
if (_deferLoad)
|
||||
{
|
||||
_deferLoad = false;
|
||||
|
||||
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
||||
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowKeyErrorOnLoad = false;
|
||||
|
||||
Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||
@@ -372,7 +337,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
||||
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
||||
|
||||
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
|
||||
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
|
||||
|
||||
if (CheckScreenBounds(savedPoint))
|
||||
{
|
||||
@@ -415,6 +380,30 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
base.OnOpened(e);
|
||||
|
||||
Initialize();
|
||||
|
||||
ViewModel.Initialize(
|
||||
ContentManager,
|
||||
StorageProvider,
|
||||
ApplicationLibrary,
|
||||
VirtualFileSystem,
|
||||
AccountManager,
|
||||
InputManager,
|
||||
_userChannelPersistence,
|
||||
LibHacHorizonManager,
|
||||
UiHandler,
|
||||
ShowLoading,
|
||||
SwitchToGameControl,
|
||||
SetMainContent,
|
||||
this);
|
||||
|
||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||
|
||||
ViewModel.RefreshFirmwareStatus();
|
||||
|
||||
LoadApplications();
|
||||
|
||||
CheckLaunchState();
|
||||
}
|
||||
|
||||
@@ -514,18 +503,15 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
});
|
||||
}
|
||||
|
||||
public async void LoadApplications()
|
||||
public void LoadApplications()
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ViewModel.Applications.Clear();
|
||||
ViewModel.Applications.Clear();
|
||||
|
||||
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
|
||||
ReloadGameList();
|
||||
}
|
||||
@@ -558,9 +544,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
_isLoading = true;
|
||||
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
||||
Thread applicationLibraryThread = new(() =>
|
||||
{
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
|
||||
|
||||
_isLoading = false;
|
||||
_isLoading = false;
|
||||
})
|
||||
{
|
||||
Name = "GUI.ApplicationLibraryThread",
|
||||
IsBackground = true,
|
||||
};
|
||||
applicationLibraryThread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -25,11 +24,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
IconImage = new Bitmap(stream);
|
||||
}
|
||||
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
@@ -192,7 +192,7 @@ namespace Ryujinx.Common.Collections
|
||||
{
|
||||
if (start.CompareTo(overlap.End) < 0)
|
||||
{
|
||||
if (overlaps.Length >= overlapCount)
|
||||
if (overlaps.Length <= overlapCount)
|
||||
{
|
||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
using Ryujinx.Cpu.AppleHv.Arm;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvAddressSpace : IDisposable
|
||||
{
|
||||
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
||||
|
@@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvAddressSpaceRange : IDisposable
|
||||
{
|
||||
private const ulong AllocationGranule = 1UL << 14;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
@@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
|
||||
enum HvExitReason : uint
|
||||
{
|
||||
Canceled,
|
||||
Exception,
|
||||
VTimerActivated,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
struct HvVcpuExit
|
||||
{
|
||||
#pragma warning disable CS0649 // Field is never assigned to
|
||||
public uint Reason;
|
||||
public HvExitReason Reason;
|
||||
public HvVcpuExitException Exception;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
@@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
static partial class HvApi
|
||||
{
|
||||
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
||||
|
@@ -1,7 +1,9 @@
|
||||
using ARMeilleure.Memory;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvCpuContext : ICpuContext
|
||||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
|
@@ -1,7 +1,9 @@
|
||||
using ARMeilleure.Memory;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
public class HvEngine : ICpuEngine
|
||||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
|
@@ -2,9 +2,12 @@ using ARMeilleure.State;
|
||||
using Ryujinx.Cpu.AppleHv.Arm;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvExecutionContext : IExecutionContext
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
@@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
|
||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||
|
||||
private int _interruptRequested;
|
||||
|
||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
_counter = counter;
|
||||
@@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
/// <inheritdoc/>
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
_impl.RequestInterrupt();
|
||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
|
||||
{
|
||||
impl.RequestInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetAndClearInterruptRequested()
|
||||
{
|
||||
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
||||
|
||||
uint reason = vcpu.ExitInfo->Reason;
|
||||
HvExitReason reason = vcpu.ExitInfo->Reason;
|
||||
|
||||
if (reason == 1)
|
||||
if (reason == HvExitReason.Exception)
|
||||
{
|
||||
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
|
||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||
@@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
address = SynchronousException(memoryManager, ref vcpu);
|
||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||
}
|
||||
else if (reason == 0)
|
||||
else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
|
||||
{
|
||||
if (_impl.GetAndClearInterruptRequested())
|
||||
if (GetAndClearInterruptRequested())
|
||||
{
|
||||
ReturnToPool(vcpu);
|
||||
InterruptHandler();
|
||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||
}
|
||||
|
||||
if (reason == HvExitReason.VTimerActivated)
|
||||
{
|
||||
vcpu.EnableAndUpdateVTimer();
|
||||
|
||||
// Unmask VTimer interrupts.
|
||||
HvApi.hv_vcpu_set_vtimer_mask(vcpu.Handle, false).ThrowOnError();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
_v[index] = value;
|
||||
}
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
}
|
||||
|
||||
public bool GetAndClearInterruptRequested()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,11 @@ using ARMeilleure.State;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvExecutionContextVcpu : IHvExecutionContext
|
||||
{
|
||||
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
||||
@@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
}
|
||||
|
||||
private readonly ulong _vcpu;
|
||||
private int _interruptRequested;
|
||||
|
||||
public HvExecutionContextVcpu(ulong vcpu)
|
||||
{
|
||||
@@ -181,16 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
||||
{
|
||||
ulong vcpu = _vcpu;
|
||||
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetAndClearInterruptRequested()
|
||||
{
|
||||
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
||||
ulong vcpu = _vcpu;
|
||||
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
readonly struct HvMemoryBlockAllocation : IDisposable
|
||||
{
|
||||
private readonly HvMemoryBlockAllocator _owner;
|
||||
|
@@ -1,7 +1,9 @@
|
||||
using Ryujinx.Memory;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
||||
{
|
||||
public class Block : PrivateMemoryAllocator.Block
|
||||
|
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
@@ -14,6 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("macos")]
|
||||
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
|
@@ -1,7 +1,15 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
unsafe class HvVcpu
|
||||
{
|
||||
private const ulong InterruptIntervalNs = 16 * 1000000; // 16 ms
|
||||
|
||||
private static ulong _interruptTimeDeltaTicks = 0;
|
||||
|
||||
public readonly ulong Handle;
|
||||
public readonly HvVcpuExit* ExitInfo;
|
||||
public readonly IHvExecutionContext ShadowContext;
|
||||
@@ -21,5 +29,28 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
NativeContext = nativeContext;
|
||||
IsEphemeral = isEphemeral;
|
||||
}
|
||||
|
||||
public void EnableAndUpdateVTimer()
|
||||
{
|
||||
// We need to ensure interrupts will be serviced,
|
||||
// and for that we set up the VTime to trigger an interrupt at fixed intervals.
|
||||
|
||||
ulong deltaTicks = _interruptTimeDeltaTicks;
|
||||
|
||||
if (deltaTicks == 0)
|
||||
{
|
||||
// Calculate our time delta in ticks based on the current clock frequency.
|
||||
|
||||
int result = TimeApi.mach_timebase_info(out var timeBaseInfo);
|
||||
|
||||
Debug.Assert(result == 0);
|
||||
|
||||
deltaTicks = ((InterruptIntervalNs * timeBaseInfo.Denom) + (timeBaseInfo.Numer - 1)) / timeBaseInfo.Numer;
|
||||
_interruptTimeDeltaTicks = deltaTicks;
|
||||
}
|
||||
|
||||
HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CTL_EL0, 1).ThrowOnError();
|
||||
HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CVAL_EL0, TimeApi.mach_absolute_time() + deltaTicks).ThrowOnError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvVcpuPool
|
||||
{
|
||||
// Since there's a limit on the number of VCPUs we can create,
|
||||
@@ -81,6 +83,8 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
|
||||
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
|
||||
|
||||
vcpu.EnableAndUpdateVTimer();
|
||||
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
static class HvVm
|
||||
{
|
||||
// This alignment allows us to use larger blocks on the page table.
|
||||
|
@@ -2,7 +2,7 @@ using ARMeilleure.State;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
public interface IHvExecutionContext
|
||||
interface IHvExecutionContext
|
||||
{
|
||||
ulong Pc { get; set; }
|
||||
ulong ElrEl1 { get; set; }
|
||||
@@ -39,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
SetV(i, context.GetV(i));
|
||||
}
|
||||
}
|
||||
|
||||
void RequestInterrupt();
|
||||
bool GetAndClearInterruptRequested();
|
||||
}
|
||||
}
|
||||
|
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
struct MachTimebaseInfo
|
||||
{
|
||||
public uint Numer;
|
||||
public uint Denom;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
static partial class TimeApi
|
||||
{
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
public static partial ulong mach_absolute_time();
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
public static partial int mach_timebase_info(out MachTimebaseInfo info);
|
||||
}
|
||||
}
|
@@ -696,11 +696,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
}
|
||||
|
||||
// Find view compatible matches.
|
||||
int overlapsCount;
|
||||
int overlapsCount = 0;
|
||||
|
||||
lock (_textures)
|
||||
if (info.Target != Target.TextureBuffer)
|
||||
{
|
||||
overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
|
||||
lock (_textures)
|
||||
{
|
||||
overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
|
||||
}
|
||||
}
|
||||
|
||||
if (_overlapInfo.Length != _textureOverlaps.Length)
|
||||
|
@@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
private int[] _allOffsets;
|
||||
private int[] _sliceSizes;
|
||||
private readonly bool _is3D;
|
||||
private readonly bool _isBuffer;
|
||||
private bool _hasMipViews;
|
||||
private bool _hasLayerViews;
|
||||
private readonly int _layers;
|
||||
@@ -118,6 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_physicalMemory = physicalMemory;
|
||||
|
||||
_is3D = storage.Info.Target == Target.Texture3D;
|
||||
_isBuffer = storage.Info.Target == Target.TextureBuffer;
|
||||
_layers = storage.Info.GetSlices();
|
||||
_levels = storage.Info.Levels;
|
||||
|
||||
@@ -794,7 +796,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
||||
int targetLevelHandles = _hasMipViews ? levels : 1;
|
||||
|
||||
if (_is3D)
|
||||
if (_isBuffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (_is3D)
|
||||
{
|
||||
// Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
|
||||
|
||||
@@ -1327,7 +1333,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
TextureGroupHandle[] handles;
|
||||
|
||||
if (!(_hasMipViews || _hasLayerViews))
|
||||
if (_isBuffer)
|
||||
{
|
||||
handles = Array.Empty<TextureGroupHandle>();
|
||||
}
|
||||
else if (!(_hasMipViews || _hasLayerViews))
|
||||
{
|
||||
// Single dirty region.
|
||||
var cpuRegionHandles = new RegionHandle[TextureRange.Count];
|
||||
|
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
public bool InUse;
|
||||
public bool InConsumption;
|
||||
public int SubmissionCount;
|
||||
public CommandBuffer CommandBuffer;
|
||||
public FenceHolder Fence;
|
||||
public SemaphoreHolder Semaphore;
|
||||
@@ -193,6 +194,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _commandBuffers[cbIndex].Fence;
|
||||
}
|
||||
|
||||
public int GetSubmissionCount(int cbIndex)
|
||||
{
|
||||
return _commandBuffers[cbIndex].SubmissionCount;
|
||||
}
|
||||
|
||||
private int FreeConsumed(bool wait)
|
||||
{
|
||||
int freeEntry = 0;
|
||||
@@ -282,6 +288,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
|
||||
entry.InUse = false;
|
||||
entry.InConsumption = true;
|
||||
entry.SubmissionCount++;
|
||||
_inUseCount--;
|
||||
|
||||
var commandBuffer = entry.CommandBuffer;
|
||||
|
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetManager : IDisposable
|
||||
{
|
||||
private const uint DescriptorPoolMultiplier = 16;
|
||||
public const uint MaxSets = 16;
|
||||
|
||||
public class DescriptorPoolHolder : IDisposable
|
||||
{
|
||||
@@ -14,36 +14,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public Device Device { get; }
|
||||
|
||||
private readonly DescriptorPool _pool;
|
||||
private readonly uint _capacity;
|
||||
private int _freeDescriptors;
|
||||
private int _totalSets;
|
||||
private int _setsInUse;
|
||||
private bool _done;
|
||||
|
||||
public unsafe DescriptorPoolHolder(Vk api, Device device)
|
||||
public unsafe DescriptorPoolHolder(Vk api, Device device, ReadOnlySpan<DescriptorPoolSize> poolSizes, bool updateAfterBind)
|
||||
{
|
||||
Api = api;
|
||||
Device = device;
|
||||
|
||||
var poolSizes = new[]
|
||||
foreach (var poolSize in poolSizes)
|
||||
{
|
||||
new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.UniformTexelBuffer, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier),
|
||||
};
|
||||
|
||||
uint maxSets = (uint)poolSizes.Length * DescriptorPoolMultiplier;
|
||||
|
||||
_capacity = maxSets;
|
||||
_freeDescriptors += (int)poolSize.DescriptorCount;
|
||||
}
|
||||
|
||||
fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
|
||||
{
|
||||
var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo
|
||||
{
|
||||
SType = StructureType.DescriptorPoolCreateInfo,
|
||||
MaxSets = maxSets,
|
||||
Flags = updateAfterBind ? DescriptorPoolCreateFlags.UpdateAfterBindBit : DescriptorPoolCreateFlags.None,
|
||||
MaxSets = MaxSets,
|
||||
PoolSizeCount = (uint)poolSizes.Length,
|
||||
PPoolSizes = pPoolsSize,
|
||||
};
|
||||
@@ -52,18 +44,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts)
|
||||
public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors)
|
||||
{
|
||||
TryAllocateDescriptorSets(layouts, isTry: false, out var dsc);
|
||||
TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: false, out var dsc);
|
||||
return dsc;
|
||||
}
|
||||
|
||||
public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, out DescriptorSetCollection dsc)
|
||||
public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors, out DescriptorSetCollection dsc)
|
||||
{
|
||||
return TryAllocateDescriptorSets(layouts, isTry: true, out dsc);
|
||||
return TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: true, out dsc);
|
||||
}
|
||||
|
||||
private unsafe bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, bool isTry, out DescriptorSetCollection dsc)
|
||||
private unsafe bool TryAllocateDescriptorSets(
|
||||
ReadOnlySpan<DescriptorSetLayout> layouts,
|
||||
int consumedDescriptors,
|
||||
bool isTry,
|
||||
out DescriptorSetCollection dsc)
|
||||
{
|
||||
Debug.Assert(!_done);
|
||||
|
||||
@@ -84,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
|
||||
if (isTry && result == Result.ErrorOutOfPoolMemory)
|
||||
{
|
||||
_totalSets = (int)_capacity;
|
||||
_totalSets = (int)MaxSets;
|
||||
_done = true;
|
||||
DestroyIfDone();
|
||||
dsc = default;
|
||||
@@ -95,6 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
_freeDescriptors -= consumedDescriptors;
|
||||
_totalSets += layouts.Length;
|
||||
_setsInUse += layouts.Length;
|
||||
|
||||
@@ -109,9 +106,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
DestroyIfDone();
|
||||
}
|
||||
|
||||
public bool CanFit(int count)
|
||||
public bool CanFit(int setsCount, int descriptorsCount)
|
||||
{
|
||||
if (_totalSets + count <= _capacity)
|
||||
// Try to determine if an allocation with the given parameters will succeed.
|
||||
// An allocation may fail if the sets count or descriptors count exceeds the available counts
|
||||
// of the pool.
|
||||
// Not getting that right is not fatal, it will just create a new pool and try again,
|
||||
// but it is less efficient.
|
||||
|
||||
if (_totalSets + setsCount <= MaxSets && _freeDescriptors >= descriptorsCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -148,46 +151,74 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
private readonly Device _device;
|
||||
private DescriptorPoolHolder _currentPool;
|
||||
private readonly DescriptorPoolHolder[] _currentPools;
|
||||
|
||||
public DescriptorSetManager(Device device)
|
||||
public DescriptorSetManager(Device device, int poolCount)
|
||||
{
|
||||
_device = device;
|
||||
_currentPools = new DescriptorPoolHolder[poolCount];
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSet(Vk api, DescriptorSetLayout layout)
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSet(
|
||||
Vk api,
|
||||
DescriptorSetLayout layout,
|
||||
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||
int poolIndex,
|
||||
int consumedDescriptors,
|
||||
bool updateAfterBind)
|
||||
{
|
||||
Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
|
||||
layouts[0] = layout;
|
||||
return AllocateDescriptorSets(api, layouts);
|
||||
return AllocateDescriptorSets(api, layouts, poolSizes, poolIndex, consumedDescriptors, updateAfterBind);
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSets(Vk api, ReadOnlySpan<DescriptorSetLayout> layouts)
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSets(
|
||||
Vk api,
|
||||
ReadOnlySpan<DescriptorSetLayout> layouts,
|
||||
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||
int poolIndex,
|
||||
int consumedDescriptors,
|
||||
bool updateAfterBind)
|
||||
{
|
||||
// If we fail the first time, just create a new pool and try again.
|
||||
if (!GetPool(api, layouts.Length).TryAllocateDescriptorSets(layouts, out var dsc))
|
||||
|
||||
var pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
|
||||
if (!pool.TryAllocateDescriptorSets(layouts, consumedDescriptors, out var dsc))
|
||||
{
|
||||
dsc = GetPool(api, layouts.Length).AllocateDescriptorSets(layouts);
|
||||
pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
|
||||
dsc = pool.AllocateDescriptorSets(layouts, consumedDescriptors);
|
||||
}
|
||||
|
||||
return new Auto<DescriptorSetCollection>(dsc);
|
||||
}
|
||||
|
||||
private DescriptorPoolHolder GetPool(Vk api, int requiredCount)
|
||||
private DescriptorPoolHolder GetPool(
|
||||
Vk api,
|
||||
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||
int poolIndex,
|
||||
int setsCount,
|
||||
int descriptorsCount,
|
||||
bool updateAfterBind)
|
||||
{
|
||||
if (_currentPool == null || !_currentPool.CanFit(requiredCount))
|
||||
ref DescriptorPoolHolder currentPool = ref _currentPools[poolIndex];
|
||||
|
||||
if (currentPool == null || !currentPool.CanFit(setsCount, descriptorsCount))
|
||||
{
|
||||
_currentPool = new DescriptorPoolHolder(api, _device);
|
||||
currentPool = new DescriptorPoolHolder(api, _device, poolSizes, updateAfterBind);
|
||||
}
|
||||
|
||||
return _currentPool;
|
||||
return currentPool;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_currentPool?.Dispose();
|
||||
for (int index = 0; index < _currentPools.Length; index++)
|
||||
{
|
||||
_currentPools[index]?.Dispose();
|
||||
_currentPools[index] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -59,6 +59,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private BitMapStruct<Array2<long>> _uniformMirrored;
|
||||
private BitMapStruct<Array2<long>> _storageMirrored;
|
||||
|
||||
private bool _updateDescriptorCacheCbIndex;
|
||||
|
||||
[Flags]
|
||||
private enum DirtyFlags
|
||||
{
|
||||
@@ -218,6 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public void SetProgram(ShaderCollection program)
|
||||
{
|
||||
_program = program;
|
||||
_updateDescriptorCacheCbIndex = true;
|
||||
_dirty = DirtyFlags.All;
|
||||
}
|
||||
|
||||
@@ -490,7 +493,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||
|
||||
var dsc = program.GetNewDescriptorSetCollection(_gd, cbs.CommandBufferIndex, setIndex, out var isNew).Get(cbs);
|
||||
if (_updateDescriptorCacheCbIndex)
|
||||
{
|
||||
_updateDescriptorCacheCbIndex = false;
|
||||
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||
}
|
||||
|
||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
|
||||
|
||||
if (!program.HasMinimalLayout)
|
||||
{
|
||||
@@ -697,6 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void SignalCommandBufferChange()
|
||||
{
|
||||
_updateDescriptorCacheCbIndex = true;
|
||||
_dirty = DirtyFlags.All;
|
||||
|
||||
_uniformSet.Clear();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Device _device;
|
||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||
private readonly int _blockAlignment;
|
||||
private readonly ReaderWriterLockSlim _lock;
|
||||
|
||||
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
||||
{
|
||||
@@ -21,6 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_device = device;
|
||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
||||
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||
}
|
||||
|
||||
public MemoryAllocation AllocateDeviceMemory(
|
||||
@@ -40,21 +43,37 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
||||
{
|
||||
for (int i = 0; i < _blockLists.Count; i++)
|
||||
_lock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
var bl = _blockLists[i];
|
||||
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||
for (int i = 0; i < _blockLists.Count; i++)
|
||||
{
|
||||
lock (bl)
|
||||
var bl = _blockLists[i];
|
||||
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||
{
|
||||
return bl.Allocate(size, alignment, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitReadLock();
|
||||
}
|
||||
|
||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||
_blockLists.Add(newBl);
|
||||
return newBl.Allocate(size, alignment, map);
|
||||
_lock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||
_blockLists.Add(newBl);
|
||||
|
||||
return newBl.Allocate(size, alignment, map);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
internal int FindSuitableMemoryTypeIndex(
|
||||
|
@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -166,6 +167,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private readonly int _blockAlignment;
|
||||
|
||||
private readonly ReaderWriterLockSlim _lock;
|
||||
|
||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||
{
|
||||
_blocks = new List<Block>();
|
||||
@@ -174,6 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
MemoryTypeIndex = memoryTypeIndex;
|
||||
ForBuffer = forBuffer;
|
||||
_blockAlignment = blockAlignment;
|
||||
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||
}
|
||||
|
||||
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
||||
@@ -184,19 +188,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < _blocks.Count; i++)
|
||||
{
|
||||
var block = _blocks[i];
|
||||
_lock.EnterReadLock();
|
||||
|
||||
if (block.Mapped == map && block.Size >= size)
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _blocks.Count; i++)
|
||||
{
|
||||
ulong offset = block.Allocate(size, alignment);
|
||||
if (offset != InvalidOffset)
|
||||
var block = _blocks[i];
|
||||
|
||||
if (block.Mapped == map && block.Size >= size)
|
||||
{
|
||||
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
||||
ulong offset = block.Allocate(size, alignment);
|
||||
if (offset != InvalidOffset)
|
||||
{
|
||||
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitReadLock();
|
||||
}
|
||||
|
||||
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
||||
|
||||
@@ -244,14 +257,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (block.IsTotallyFree())
|
||||
{
|
||||
for (int i = 0; i < _blocks.Count; i++)
|
||||
_lock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
if (_blocks[i] == block)
|
||||
for (int i = 0; i < _blocks.Count; i++)
|
||||
{
|
||||
_blocks.RemoveAt(i);
|
||||
break;
|
||||
if (_blocks[i] == block)
|
||||
{
|
||||
_blocks.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
block.Destroy(_api, _device);
|
||||
}
|
||||
@@ -259,13 +281,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private void InsertBlock(Block block)
|
||||
{
|
||||
int index = _blocks.BinarySearch(block);
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
_lock.EnterWriteLock();
|
||||
|
||||
_blocks.Insert(index, block);
|
||||
try
|
||||
{
|
||||
int index = _blocks.BinarySearch(block);
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
_blocks.Insert(index, block);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -7,15 +8,28 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class PipelineLayoutCacheEntry
|
||||
{
|
||||
// Those were adjusted based on current descriptor usage and the descriptor counts usually used on pipeline layouts.
|
||||
// It might be a good idea to tweak them again if those change, or maybe find a way to calculate an optimal value dynamically.
|
||||
private const uint DefaultUniformBufferPoolCapacity = 19 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultStorageBufferPoolCapacity = 16 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultTexturePoolCapacity = 128 * DescriptorSetManager.MaxSets;
|
||||
private const uint DefaultImagePoolCapacity = 8 * DescriptorSetManager.MaxSets;
|
||||
|
||||
private const int MaxPoolSizesPerSet = 2;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
||||
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
||||
public PipelineLayout PipelineLayout { get; }
|
||||
|
||||
private readonly int[] _consumedDescriptorsPerSet;
|
||||
|
||||
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
|
||||
private List<Auto<DescriptorSetCollection>>[] _currentDsCache;
|
||||
private readonly int[] _dsCacheCursor;
|
||||
private int _dsLastCbIndex;
|
||||
private int _dsLastSubmissionCount;
|
||||
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||
{
|
||||
@@ -44,29 +58,55 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
||||
{
|
||||
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||
|
||||
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
||||
|
||||
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (var descriptor in setDescriptors[setIndex].Descriptors)
|
||||
{
|
||||
count += descriptor.Count;
|
||||
}
|
||||
|
||||
_consumedDescriptorsPerSet[setIndex] = count;
|
||||
}
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
||||
VulkanRenderer gd,
|
||||
int commandBufferIndex,
|
||||
int setIndex,
|
||||
out bool isNew)
|
||||
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||
{
|
||||
if (_dsLastCbIndex != commandBufferIndex)
|
||||
int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
|
||||
|
||||
if (_dsLastCbIndex != commandBufferIndex || _dsLastSubmissionCount != submissionCount)
|
||||
{
|
||||
_dsLastCbIndex = commandBufferIndex;
|
||||
|
||||
for (int i = 0; i < _dsCacheCursor.Length; i++)
|
||||
{
|
||||
_dsCacheCursor[i] = 0;
|
||||
}
|
||||
_dsLastSubmissionCount = submissionCount;
|
||||
Array.Clear(_dsCacheCursor);
|
||||
}
|
||||
|
||||
var list = _dsCache[commandBufferIndex][setIndex];
|
||||
_currentDsCache = _dsCache[commandBufferIndex];
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
|
||||
{
|
||||
var list = _currentDsCache[setIndex];
|
||||
int index = _dsCacheCursor[setIndex]++;
|
||||
if (index == list.Count)
|
||||
{
|
||||
var dsc = gd.DescriptorSetManager.AllocateDescriptorSet(gd.Api, DescriptorSetLayouts[setIndex]);
|
||||
Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
|
||||
poolSizes = GetDescriptorPoolSizes(poolSizes, setIndex);
|
||||
|
||||
int consumedDescriptors = _consumedDescriptorsPerSet[setIndex];
|
||||
|
||||
var dsc = _gd.DescriptorSetManager.AllocateDescriptorSet(
|
||||
_gd.Api,
|
||||
DescriptorSetLayouts[setIndex],
|
||||
poolSizes,
|
||||
setIndex,
|
||||
consumedDescriptors,
|
||||
false);
|
||||
|
||||
list.Add(dsc);
|
||||
isNew = true;
|
||||
return dsc;
|
||||
@@ -76,6 +116,33 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return list[index];
|
||||
}
|
||||
|
||||
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, int setIndex)
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
switch (setIndex)
|
||||
{
|
||||
case PipelineBase.UniformSetIndex:
|
||||
output[0] = new(DescriptorType.UniformBuffer, DefaultUniformBufferPoolCapacity);
|
||||
break;
|
||||
case PipelineBase.StorageSetIndex:
|
||||
output[0] = new(DescriptorType.StorageBuffer, DefaultStorageBufferPoolCapacity);
|
||||
break;
|
||||
case PipelineBase.TextureSetIndex:
|
||||
output[0] = new(DescriptorType.CombinedImageSampler, DefaultTexturePoolCapacity);
|
||||
output[1] = new(DescriptorType.UniformTexelBuffer, DefaultTexturePoolCapacity);
|
||||
count = 2;
|
||||
break;
|
||||
case PipelineBase.ImageSetIndex:
|
||||
output[0] = new(DescriptorType.StorageImage, DefaultImagePoolCapacity);
|
||||
output[1] = new(DescriptorType.StorageTexelBuffer, DefaultImagePoolCapacity);
|
||||
count = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return output[..count];
|
||||
}
|
||||
|
||||
protected virtual unsafe void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@@ -464,13 +464,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return true;
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
||||
VulkanRenderer gd,
|
||||
int commandBufferIndex,
|
||||
int setIndex,
|
||||
out bool isNew)
|
||||
public void UpdateDescriptorCacheCommandBufferIndex(int commandBufferIndex)
|
||||
{
|
||||
return _plce.GetNewDescriptorSetCollection(gd, commandBufferIndex, setIndex, out isNew);
|
||||
_plce.UpdateCommandBufferIndex(commandBufferIndex);
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
|
||||
{
|
||||
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
|
@@ -347,7 +347,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||
|
||||
DescriptorSetManager = new DescriptorSetManager(_device);
|
||||
DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
|
||||
|
||||
PipelineLayoutCache = new PipelineLayoutCache();
|
||||
|
||||
|
Reference in New Issue
Block a user