Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4a835bb2b9 | ||
|
ddc9ae2a83 | ||
|
d6d3cdd573 |
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
"Light" => ThemeVariant.Light,
|
"Light" => ThemeVariant.Light,
|
||||||
"Dark" => ThemeVariant.Dark,
|
"Dark" => ThemeVariant.Dark,
|
||||||
_ => ThemeVariant.Default
|
_ => ThemeVariant.Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (enableCustomTheme)
|
if (enableCustomTheme)
|
||||||
|
@@ -3,7 +3,6 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Rendering;
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Audio.Backends.Dummy;
|
using Ryujinx.Audio.Backends.Dummy;
|
||||||
|
@@ -145,7 +145,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count == 0)
|
if (result.Count == 0)
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
|
@@ -82,12 +82,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
|
||||||
});
|
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
@@ -114,10 +111,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -134,10 +130,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -149,10 +144,8 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
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;
|
_running = false;
|
||||||
|
|
||||||
@@ -167,12 +160,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
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],
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
|
||||||
});
|
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
@@ -183,10 +173,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -212,7 +201,7 @@ namespace Ryujinx.Modules
|
|||||||
_buildSize = -1;
|
_buildSize = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
// Show a message asking the user if they want to update
|
// Show a message asking the user if they want to update
|
||||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
||||||
@@ -222,7 +211,7 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
if (shouldUpdate)
|
if (shouldUpdate)
|
||||||
{
|
{
|
||||||
UpdateRyujinx(mainWindow, _buildUrl);
|
await UpdateRyujinx(mainWindow, _buildUrl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -241,7 +230,7 @@ namespace Ryujinx.Modules
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void UpdateRyujinx(Window parent, string downloadUrl)
|
private static async Task UpdateRyujinx(Window parent, string downloadUrl)
|
||||||
{
|
{
|
||||||
_updateSuccessful = false;
|
_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
|
// Extract Update
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
await Task.Run(() =>
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
||||||
{
|
}
|
||||||
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
else if (OperatingSystem.IsWindows())
|
||||||
}
|
{
|
||||||
else if (OperatingSystem.IsWindows())
|
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
||||||
{
|
}
|
||||||
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
else
|
||||||
}
|
{
|
||||||
else
|
throw new NotSupportedException();
|
||||||
{
|
}
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete downloaded zip
|
// Delete downloaded zip
|
||||||
File.Delete(updateFile);
|
File.Delete(updateFile);
|
||||||
@@ -613,36 +599,33 @@ namespace Ryujinx.Modules
|
|||||||
if (!OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
// Replace old files
|
// Replace old files
|
||||||
await Task.Run(() =>
|
double count = 0;
|
||||||
|
foreach (string file in allFiles)
|
||||||
{
|
{
|
||||||
double count = 0;
|
count++;
|
||||||
foreach (string file in allFiles)
|
try
|
||||||
{
|
{
|
||||||
count++;
|
File.Move(file, file + ".ryuold");
|
||||||
try
|
|
||||||
{
|
|
||||||
File.Move(file, file + ".ryuold");
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
{
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
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);
|
Directory.Delete(_updateDir, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,12 +641,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -673,12 +655,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -688,12 +669,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -705,21 +685,19 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (ReleaseInformation.IsFlatHubBuild())
|
if (ReleaseInformation.IsFlatHubBuild())
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -106,7 +106,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
bool error = false;
|
bool error = false;
|
||||||
string inputText = args.InitialText ?? "";
|
string inputText = args.InitialText ?? "";
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -149,7 +149,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool showDetails = false;
|
bool showDetails = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
|
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;
|
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)
|
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
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)
|
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
|
||||||
@@ -158,11 +158,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
@@ -205,11 +206,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
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;
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
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" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var file in result)
|
foreach (var file in result)
|
||||||
|
@@ -26,6 +26,7 @@ using Ryujinx.HLE.FileSystem;
|
|||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.Ui;
|
using Ryujinx.HLE.Ui;
|
||||||
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
@@ -39,7 +40,6 @@ using System.IO;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
|
||||||
using Key = Ryujinx.Input.Key;
|
using Key = Ryujinx.Input.Key;
|
||||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||||
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
||||||
@@ -1068,9 +1068,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
||||||
|
|
||||||
static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1163,16 +1161,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AppHost?.DisposeContext();
|
AppHost?.DisposeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleRelaunch()
|
private async Task HandleRelaunch()
|
||||||
{
|
{
|
||||||
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
|
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
|
||||||
{
|
{
|
||||||
UserChannelPersistence.ShouldRestart = false;
|
UserChannelPersistence.ShouldRestart = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
await LoadApplication(_currentEmulatedGamePath);
|
||||||
{
|
|
||||||
LoadApplication(_currentEmulatedGamePath);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1191,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Application.Current.Styles.TryGetResource(args.VSyncEnabled
|
Application.Current.Styles.TryGetResource(args.VSyncEnabled
|
||||||
? "VsyncEnabled"
|
? "VsyncEnabled"
|
||||||
: "VsyncDisabled",
|
: "VsyncDisabled",
|
||||||
Avalonia.Application.Current.ActualThemeVariant,
|
Application.Current.ActualThemeVariant,
|
||||||
out object color);
|
out object color);
|
||||||
|
|
||||||
if (color is not null)
|
if (color is not null)
|
||||||
@@ -1283,7 +1278,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Glyph = Glyph.Grid;
|
Glyph = Glyph.Grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void InstallFirmwareFromFile()
|
public async Task InstallFirmwareFromFile()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -1294,21 +1289,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci", "*.zip" },
|
Patterns = new[] { "*.xci", "*.zip" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
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")
|
new("XCI")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci" },
|
Patterns = new[] { "*.xci" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||||
MimeTypes = new[] { "application/x-nx-xci" }
|
MimeTypes = new[] { "application/x-nx-xci" },
|
||||||
},
|
},
|
||||||
new("ZIP")
|
new("ZIP")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.zip" },
|
Patterns = new[] { "*.zip" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
||||||
MimeTypes = new[] { "application/zip" }
|
MimeTypes = new[] { "application/zip" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
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
|
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
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)
|
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);
|
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
||||||
}
|
}
|
||||||
@@ -1387,7 +1382,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AppHost.Device.System.SimulateWakeUpMessage();
|
AppHost.Device.System.SimulateWakeUpMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenFile()
|
public async Task OpenFile()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -1404,7 +1399,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
"com.ryujinx.xci",
|
"com.ryujinx.xci",
|
||||||
"com.ryujinx.nca",
|
"com.ryujinx.nca",
|
||||||
"com.ryujinx.nro",
|
"com.ryujinx.nro",
|
||||||
"com.ryujinx.nso"
|
"com.ryujinx.nso",
|
||||||
},
|
},
|
||||||
MimeTypes = new[]
|
MimeTypes = new[]
|
||||||
{
|
{
|
||||||
@@ -1412,63 +1407,63 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
"application/x-nx-xci",
|
"application/x-nx-xci",
|
||||||
"application/x-nx-nca",
|
"application/x-nx-nca",
|
||||||
"application/x-nx-nro",
|
"application/x-nx-nro",
|
||||||
"application/x-nx-nso"
|
"application/x-nx-nso",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
new("NSP")
|
new("NSP")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nsp" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
},
|
},
|
||||||
new("XCI")
|
new("XCI")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci" },
|
Patterns = new[] { "*.xci" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||||
MimeTypes = new[] { "application/x-nx-xci" }
|
MimeTypes = new[] { "application/x-nx-xci" },
|
||||||
},
|
},
|
||||||
new("NCA")
|
new("NCA")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nca" },
|
Patterns = new[] { "*.nca" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
||||||
MimeTypes = new[] { "application/x-nx-nca" }
|
MimeTypes = new[] { "application/x-nx-nca" },
|
||||||
},
|
},
|
||||||
new("NRO")
|
new("NRO")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nro" },
|
Patterns = new[] { "*.nro" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
||||||
MimeTypes = new[] { "application/x-nx-nro" }
|
MimeTypes = new[] { "application/x-nx-nro" },
|
||||||
},
|
},
|
||||||
new("NSO")
|
new("NSO")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nso" },
|
Patterns = new[] { "*.nso" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
||||||
MimeTypes = new[] { "application/x-nx-nso" }
|
MimeTypes = new[] { "application/x-nx-nso" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
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
|
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
|
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
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)
|
if (AppHost != null)
|
||||||
{
|
{
|
||||||
@@ -1505,35 +1500,30 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
this,
|
this,
|
||||||
TopLevel);
|
TopLevel);
|
||||||
|
|
||||||
async void Action()
|
if (!await AppHost.LoadGuestApplication())
|
||||||
{
|
{
|
||||||
if (!await AppHost.LoadGuestApplication())
|
AppHost.DisposeContext();
|
||||||
{
|
AppHost = null;
|
||||||
AppHost.DisposeContext();
|
|
||||||
AppHost = null;
|
|
||||||
|
|
||||||
return;
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
public void SwitchToRenderer(bool startFullscreen)
|
||||||
@@ -1596,7 +1586,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
IsGameRunning = false;
|
IsGameRunning = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = true;
|
ShowMenuAndStatusBar = true;
|
||||||
ShowContent = true;
|
ShowContent = true;
|
||||||
@@ -1609,7 +1599,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
AppHost = null;
|
AppHost = null;
|
||||||
|
|
||||||
HandleRelaunch();
|
await HandleRelaunch();
|
||||||
});
|
});
|
||||||
|
|
||||||
RendererHostControl.WindowCreated -= RendererHost_Created;
|
RendererHostControl.WindowCreated -= RendererHost_Created;
|
||||||
|
@@ -78,14 +78,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
|
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
@@ -22,6 +22,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
@@ -184,18 +185,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +202,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
SortUpdates();
|
SortUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Add()
|
public async Task Add()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -218,9 +213,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.nsp" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var file in result)
|
foreach (var file in result)
|
||||||
|
@@ -17,7 +17,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Views.Main
|
namespace Ryujinx.Ava.UI.Views.Main
|
||||||
{
|
{
|
||||||
@@ -107,20 +106,14 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
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)
|
public async void OpenSettings(object sender, RoutedEventArgs e)
|
||||||
@@ -132,13 +125,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
ViewModel.LoadConfigurableHotKeys();
|
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);
|
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(contentPath))
|
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())
|
if (FileAssociationHelper.Install())
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,8 +201,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
if (FileAssociationHelper.Uninstall())
|
if (FileAssociationHelper.Uninstall())
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
{
|
{
|
||||||
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.xaml" },
|
Patterns = new[] { "*.xaml" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
|
||||||
MimeTypes = new[] { "application/xaml+xml" }
|
MimeTypes = new[] { "application/xaml+xml" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
|
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.User
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.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)
|
if (result.Count > 0)
|
||||||
|
@@ -25,6 +25,7 @@ using Ryujinx.Ui.Common.Helper;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
@@ -79,35 +80,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
Initialize();
|
|
||||||
|
|
||||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
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.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
||||||
this.ScalingChanged += OnScalingChanged;
|
this.ScalingChanged += OnScalingChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
|
||||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
@@ -122,36 +99,17 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.IsActive = obj;
|
ViewModel.IsActive = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadGameList()
|
|
||||||
{
|
|
||||||
if (_isLoading)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isLoading = true;
|
|
||||||
|
|
||||||
LoadApplications();
|
|
||||||
|
|
||||||
_isLoading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnScalingChanged(object sender, EventArgs e)
|
private void OnScalingChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Program.DesktopScaleFactor = this.RenderScaling;
|
Program.DesktopScaleFactor = this.RenderScaling;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddApplication(ApplicationData applicationData)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
ViewModel.Applications.Add(applicationData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
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)
|
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;
|
string path = new FileInfo(args.Application.Path).FullName;
|
||||||
|
|
||||||
ViewModel.LoadApplication(path);
|
ViewModel.LoadApplication(path).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -202,13 +160,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.ShowContent = true;
|
ViewModel.ShowContent = true;
|
||||||
ViewModel.IsLoadingIndeterminate = false;
|
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)
|
public void ShowLoading(bool startFullscreen = false)
|
||||||
@@ -217,13 +172,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.ShowLoadProgress = true;
|
ViewModel.ShowLoadProgress = true;
|
||||||
ViewModel.IsLoadingIndeterminate = 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()
|
private void Initialize()
|
||||||
@@ -255,7 +207,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static async void ShowVmMaxMapCountWarning()
|
private static async Task ShowVmMaxMapCountWarning()
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
||||||
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
||||||
@@ -267,7 +219,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static async void ShowVmMaxMapCountDialog()
|
private static async Task ShowVmMaxMapCountDialog()
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
||||||
LinuxHelper.RecommendedVmMaxMapCount);
|
LinuxHelper.RecommendedVmMaxMapCount);
|
||||||
@@ -317,8 +269,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
ShowKeyErrorOnLoad = false;
|
ShowKeyErrorOnLoad = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () => await
|
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys).Wait();
|
||||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
||||||
@@ -327,11 +278,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
if (LinuxHelper.PkExecPath is not null)
|
if (LinuxHelper.PkExecPath is not null)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
|
ShowVmMaxMapCountDialog().Wait();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
|
ShowVmMaxMapCountWarning().Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +290,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
_deferLoad = false;
|
_deferLoad = false;
|
||||||
|
|
||||||
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||||
@@ -372,7 +323,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
||||||
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * 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))
|
if (CheckScreenBounds(savedPoint))
|
||||||
{
|
{
|
||||||
@@ -415,6 +366,30 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
base.OnOpened(e);
|
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();
|
CheckLaunchState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,18 +489,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;
|
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||||
ViewModel.StatusBarProgressMaximum = 0;
|
ViewModel.StatusBarProgressMaximum = 0;
|
||||||
ViewModel.StatusBarProgressValue = 0;
|
ViewModel.StatusBarProgressValue = 0;
|
||||||
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||||
});
|
|
||||||
|
|
||||||
ReloadGameList();
|
ReloadGameList();
|
||||||
}
|
}
|
||||||
@@ -558,9 +530,17 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
_isLoading = true;
|
_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.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -25,11 +24,6 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
IconImage = new Bitmap(stream);
|
IconImage = new Bitmap(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnOpened(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnOpened(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpace : IDisposable
|
class HvAddressSpace : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
||||||
|
@@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpaceRange : IDisposable
|
class HvAddressSpaceRange : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong AllocationGranule = 1UL << 14;
|
private const ulong AllocationGranule = 1UL << 14;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
@@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HvExitReason : uint
|
||||||
|
{
|
||||||
|
Canceled,
|
||||||
|
Exception,
|
||||||
|
VTimerActivated,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
struct HvVcpuExit
|
struct HvVcpuExit
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
#pragma warning disable CS0649 // Field is never assigned to
|
||||||
public uint Reason;
|
public HvExitReason Reason;
|
||||||
public HvVcpuExitException Exception;
|
public HvVcpuExitException Exception;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
@@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static partial class HvApi
|
static partial class HvApi
|
||||||
{
|
{
|
||||||
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvCpuContext : ICpuContext
|
class HvCpuContext : ICpuContext
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvEngine : ICpuEngine
|
public class HvEngine : ICpuEngine
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
@@ -2,9 +2,12 @@ using ARMeilleure.State;
|
|||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContext : IExecutionContext
|
class HvExecutionContext : IExecutionContext
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
|
|
||||||
|
private int _interruptRequested;
|
||||||
|
|
||||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
{
|
{
|
||||||
_counter = counter;
|
_counter = counter;
|
||||||
@@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RequestInterrupt()
|
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/>
|
/// <inheritdoc/>
|
||||||
@@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
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;
|
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
|
||||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||||
@@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
address = SynchronousException(memoryManager, ref vcpu);
|
address = SynchronousException(memoryManager, ref vcpu);
|
||||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
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);
|
ReturnToPool(vcpu);
|
||||||
InterruptHandler();
|
InterruptHandler();
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
_v[index] = value;
|
_v[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestInterrupt()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,11 @@ using ARMeilleure.State;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContextVcpu : IHvExecutionContext
|
class HvExecutionContextVcpu : IHvExecutionContext
|
||||||
{
|
{
|
||||||
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
||||||
@@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly ulong _vcpu;
|
private readonly ulong _vcpu;
|
||||||
private int _interruptRequested;
|
|
||||||
|
|
||||||
public HvExecutionContextVcpu(ulong vcpu)
|
public HvExecutionContextVcpu(ulong vcpu)
|
||||||
{
|
{
|
||||||
@@ -181,16 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
ulong vcpu = _vcpu;
|
||||||
{
|
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
||||||
ulong vcpu = _vcpu;
|
|
||||||
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
readonly struct HvMemoryBlockAllocation : IDisposable
|
readonly struct HvMemoryBlockAllocation : IDisposable
|
||||||
{
|
{
|
||||||
private readonly HvMemoryBlockAllocator _owner;
|
private readonly HvMemoryBlockAllocator _owner;
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
||||||
{
|
{
|
||||||
public class Block : PrivateMemoryAllocator.Block
|
public class Block : PrivateMemoryAllocator.Block
|
||||||
|
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
@@ -14,6 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = 12;
|
public const int PageBits = 12;
|
||||||
|
@@ -1,7 +1,15 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
unsafe class HvVcpu
|
unsafe class HvVcpu
|
||||||
{
|
{
|
||||||
|
private const ulong InterruptIntervalNs = 16 * 1000000; // 16 ms
|
||||||
|
|
||||||
|
private static ulong _interruptTimeDeltaTicks = 0;
|
||||||
|
|
||||||
public readonly ulong Handle;
|
public readonly ulong Handle;
|
||||||
public readonly HvVcpuExit* ExitInfo;
|
public readonly HvVcpuExit* ExitInfo;
|
||||||
public readonly IHvExecutionContext ShadowContext;
|
public readonly IHvExecutionContext ShadowContext;
|
||||||
@@ -21,5 +29,28 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
NativeContext = nativeContext;
|
NativeContext = nativeContext;
|
||||||
IsEphemeral = isEphemeral;
|
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;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvVcpuPool
|
class HvVcpuPool
|
||||||
{
|
{
|
||||||
// Since there's a limit on the number of VCPUs we can create,
|
// 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);
|
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
|
||||||
|
|
||||||
|
vcpu.EnableAndUpdateVTimer();
|
||||||
|
|
||||||
return vcpu;
|
return vcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static class HvVm
|
static class HvVm
|
||||||
{
|
{
|
||||||
// This alignment allows us to use larger blocks on the page table.
|
// This alignment allows us to use larger blocks on the page table.
|
||||||
|
@@ -2,7 +2,7 @@ using ARMeilleure.State;
|
|||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
public interface IHvExecutionContext
|
interface IHvExecutionContext
|
||||||
{
|
{
|
||||||
ulong Pc { get; set; }
|
ulong Pc { get; set; }
|
||||||
ulong ElrEl1 { get; set; }
|
ulong ElrEl1 { get; set; }
|
||||||
@@ -39,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
SetV(i, context.GetV(i));
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
||||||
{
|
{
|
||||||
@@ -21,6 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_device = device;
|
_device = device;
|
||||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
@@ -40,21 +43,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
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];
|
for (int i = 0; i < _blockLists.Count; i++)
|
||||||
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
|
||||||
{
|
{
|
||||||
lock (bl)
|
var bl = _blockLists[i];
|
||||||
|
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||||
{
|
{
|
||||||
return bl.Allocate(size, alignment, map);
|
return bl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
_lock.EnterWriteLock();
|
||||||
_blockLists.Add(newBl);
|
|
||||||
return newBl.Allocate(size, alignment, map);
|
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(
|
internal int FindSuitableMemoryTypeIndex(
|
||||||
|
@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -166,6 +167,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||||
{
|
{
|
||||||
_blocks = new List<Block>();
|
_blocks = new List<Block>();
|
||||||
@@ -174,6 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
MemoryTypeIndex = memoryTypeIndex;
|
MemoryTypeIndex = memoryTypeIndex;
|
||||||
ForBuffer = forBuffer;
|
ForBuffer = forBuffer;
|
||||||
_blockAlignment = blockAlignment;
|
_blockAlignment = blockAlignment;
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
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}.");
|
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _blocks.Count; i++)
|
_lock.EnterReadLock();
|
||||||
{
|
|
||||||
var block = _blocks[i];
|
|
||||||
|
|
||||||
if (block.Mapped == map && block.Size >= size)
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _blocks.Count; i++)
|
||||||
{
|
{
|
||||||
ulong offset = block.Allocate(size, alignment);
|
var block = _blocks[i];
|
||||||
if (offset != InvalidOffset)
|
|
||||||
|
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);
|
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
||||||
|
|
||||||
@@ -244,14 +257,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (block.IsTotallyFree())
|
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);
|
if (_blocks[i] == block)
|
||||||
break;
|
{
|
||||||
|
_blocks.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
block.Destroy(_api, _device);
|
block.Destroy(_api, _device);
|
||||||
}
|
}
|
||||||
@@ -259,13 +281,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private void InsertBlock(Block block)
|
private void InsertBlock(Block block)
|
||||||
{
|
{
|
||||||
int index = _blocks.BinarySearch(block);
|
_lock.EnterWriteLock();
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index;
|
|
||||||
}
|
|
||||||
|
|
||||||
_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()
|
public void Dispose()
|
||||||
|
Reference in New Issue
Block a user