Compare commits

...

3 Commits

Author SHA1 Message Date
gdkchan
4a835bb2b9 Make Vulkan memory allocator actually thread safe (#5575)
* Make Vulkan memory allocator actually thread safe

* Make free thread safe too

* PR feedback
2023-09-26 01:50:06 +02:00
gdkchan
ddc9ae2a83 Add VTimer as alternative interrupt method on Apple Hypervisor (#5663)
* Add VTimer as alternative interrupt method on Apple Hypervisor

* Fix naming violations on TimeApi

* Fix timer interval (was 16us rather than 16ms)

* Fix delta ticks calculation

* Missing ThrowOnError call

* Add SupportedOSPlatform attribute on AppleHv classes
2023-09-26 01:18:32 +02:00
Isaac Marovitz
d6d3cdd573 Ava UI: Refactor async usage (#5516)
* Remove `async void`

* Async LoadApplications

* Formatting and such

* Remove async from InstallUpdate

* Update src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Cleanup LoadApplications()

* Cleanup

* Formatting

* Revert some stuff

* Cleanup

* Update src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Ack suggestions

* Whitespace

* Fix Peri suggestion

* Add missing trailing commas

* Remove redundant method override

* Remove Dispatcher.UIThread.InvokeAsync/Post where possible

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-09-26 00:04:58 +02:00
34 changed files with 418 additions and 358 deletions

View File

@@ -104,7 +104,7 @@ namespace Ryujinx.Ava
{
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default
_ => ThemeVariant.Default,
};
if (enableCustomTheme)

View File

@@ -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;

View File

@@ -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)

View File

@@ -1,6 +1,5 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input;
using System;

View File

@@ -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])
);
}
}

View File

@@ -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
{

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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)

View File

@@ -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
{

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);
@@ -317,8 +269,7 @@ namespace Ryujinx.Ava.UI.Windows
{
ShowKeyErrorOnLoad = false;
Dispatcher.UIThread.Post(async () => await
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys).Wait();
}
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
@@ -327,11 +278,11 @@ namespace Ryujinx.Ava.UI.Windows
if (LinuxHelper.PkExecPath is not null)
{
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
ShowVmMaxMapCountDialog().Wait();
}
else
{
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
ShowVmMaxMapCountWarning().Wait();
}
}
@@ -339,7 +290,7 @@ namespace Ryujinx.Ava.UI.Windows
{
_deferLoad = false;
ViewModel.LoadApplication(_launchPath, _startFullscreen);
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
}
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.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 +366,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 +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;
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 +530,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();
}
}
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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;

View File

@@ -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";

View File

@@ -1,7 +1,9 @@
using ARMeilleure.Memory;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvCpuContext : ICpuContext
{
private readonly ITickSource _tickSource;

View File

@@ -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;

View File

@@ -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
{

View File

@@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
{
_v[index] = value;
}
public void RequestInterrupt()
{
}
public bool GetAndClearInterruptRequested()
{
return false;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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();
}
}

View 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);
}
}

View File

@@ -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(

View File

@@ -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()