From 2efd74b9cbb43b8f67907d39f353e06f4fef3863 Mon Sep 17 00:00:00 2001 From: MutantAura <44103205+MutantAura@users.noreply.github.com> Date: Sat, 12 Aug 2023 21:43:03 +0100 Subject: [PATCH] Ava UI: Make some settings methods async (#5332) * Ava: Asynchronously load Vulkan device settings items. * Sound checks, timezones and network interface async * Refresh UI items once awaited tasks complete * Remove unused dep * Timezone UI update * Use UIThread dispatcher for thread-unsafe collections + simplify GPU collection. * Remove empty lines * Remove unused string * Dispatch property changes * format changes * format 2 * Use Tasks instead of async void * Make NetworkInterfaceIndex access thread safe. --- .../UI/ViewModels/SettingsViewModel.cs | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 1e6d2734..441c669d 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -1,7 +1,6 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Threading; -using DynamicData; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.SDL2; @@ -24,6 +23,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Net.NetworkInformation; using System.Runtime.InteropServices; +using System.Threading.Tasks; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; namespace Ryujinx.Ava.UI.ViewModels @@ -44,7 +44,7 @@ namespace Ryujinx.Ava.UI.ViewModels private float _volume; private bool _isVulkanAvailable = true; private bool _directoryChanged; - private List _gpuIds = new(); + private readonly List _gpuIds = new(); private KeyboardHotkeys _keyboardHotkeys; private int _graphicsBackendIndex; private string _customThemePath; @@ -278,7 +278,7 @@ namespace Ryujinx.Ava.UI.ViewModels _contentManager = contentManager; if (Program.PreviewerDetached) { - LoadTimeZones(); + Task.Run(LoadTimeZones); } } @@ -290,27 +290,34 @@ namespace Ryujinx.Ava.UI.ViewModels _validTzRegions = new List(); _networkInterfaces = new Dictionary(); - CheckSoundBackends(); - PopulateNetworkInterfaces(); + Task.Run(CheckSoundBackends); + Task.Run(PopulateNetworkInterfaces); if (Program.PreviewerDetached) { - LoadAvailableGpus(); + Task.Run(LoadAvailableGpus); LoadCurrentConfiguration(); } } - public void CheckSoundBackends() + public async Task CheckSoundBackends() { IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported; IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported; IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported; + + await Dispatcher.UIThread.InvokeAsync(() => + { + OnPropertyChanged(nameof(IsOpenAlEnabled)); + OnPropertyChanged(nameof(IsSoundIoEnabled)); + OnPropertyChanged(nameof(IsSDL2Enabled)); + }); } - private void LoadAvailableGpus() + private async Task LoadAvailableGpus() { - _gpuIds = new List(); - List names = new(); + AvailableGpus.Clear(); + var devices = VulkanRenderer.GetPhysicalDevices(); if (devices.Length == 0) @@ -322,16 +329,23 @@ namespace Ryujinx.Ava.UI.ViewModels { foreach (var device in devices) { - _gpuIds.Add(device.Id); - names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}"); + await Dispatcher.UIThread.InvokeAsync(() => + { + _gpuIds.Add(device.Id); + + AvailableGpus.Add(new ComboBoxItem { Content = $"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}" }); + }); } } - AvailableGpus.Clear(); - AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x })); + // GPU configuration needs to be loaded during the async method or it will always return 0. + PreferredGpuIndex = _gpuIds.Contains(ConfigurationState.Instance.Graphics.PreferredGpu) ? + _gpuIds.IndexOf(ConfigurationState.Instance.Graphics.PreferredGpu) : 0; + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex))); } - public void LoadTimeZones() + public async Task LoadTimeZones() { _timeZoneContentManager = new TimeZoneContentManager(); @@ -344,21 +358,34 @@ namespace Ryujinx.Ava.UI.ViewModels string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr; - TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2)); + await Dispatcher.UIThread.InvokeAsync(() => + { + TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2)); - _validTzRegions.Add(location); + _validTzRegions.Add(location); + }); } + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(TimeZone))); } - private void PopulateNetworkInterfaces() + private async Task PopulateNetworkInterfaces() { _networkInterfaces.Clear(); _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0"); foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces()) { - _networkInterfaces.Add(networkInterface.Name, networkInterface.Id); + await Dispatcher.UIThread.InvokeAsync(() => + { + _networkInterfaces.Add(networkInterface.Name, networkInterface.Id); + }); } + + // Network interface index needs to be loaded during the async method or it will always return 0. + NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); + + Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(NetworkInterfaceIndex))); } public void ValidateAndSetTimeZone(string location) @@ -416,7 +443,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; - PreferredGpuIndex = _gpuIds.Contains(config.Graphics.PreferredGpu) ? _gpuIds.IndexOf(config.Graphics.PreferredGpu) : 0; + // Physical devices are queried asynchronously hence the prefered index config value is loaded in LoadAvailableGpus(). EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableMacroHLE = config.Graphics.EnableMacroHLE; @@ -437,6 +464,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Network EnableInternetAccess = config.System.EnableInternetAccess; + // LAN interface index is loaded asynchronously in PopulateNetworkInterfaces() // Logging EnableFileLog = config.Logger.EnableFileLog; @@ -450,8 +478,6 @@ namespace Ryujinx.Ava.UI.ViewModels EnableFsAccessLog = config.Logger.EnableFsAccessLog; FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode; OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value; - - NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value); } public void SaveSettings()