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: 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.
---
 src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs | 70 +++++++++++++++-------
 1 file changed, 48 insertions(+), 22 deletions(-)

(limited to 'src')

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<string> _gpuIds = new();
+        private readonly List<string> _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<string>();
             _networkInterfaces = new Dictionary<string, string>();
 
-            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<string>();
-            List<string> 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()
-- 
cgit v1.2.3-70-g09d2