diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 266bdba6f1..c931aaead3 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -70,6 +70,10 @@ namespace ARMeilleure.Translation.PTC internal static PtcState State { get; private set; } + // Progress update events + public static event Action PtcTranslationStateChanged; + public static event Action PtcTranslationProgressChanged; + static Ptc() { InitializeMemoryStreams(); @@ -772,6 +776,8 @@ namespace ARMeilleure.Translation.PTC ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count); + PtcTranslationStateChanged?.Invoke(true); + void TranslateFuncs() { while (profiledFuncsToTranslate.TryDequeue(out var item)) @@ -820,6 +826,7 @@ namespace ARMeilleure.Translation.PTC threads.Clear(); _loggerEvent.Set(); + PtcTranslationStateChanged?.Invoke(false); PtcJumpTable.Initialize(jumpTable); @@ -833,15 +840,15 @@ namespace ARMeilleure.Translation.PTC private static void TranslationLogger(object state) { - const int refreshRate = 1; // Seconds. + const int refreshRate = 100; // ms int profiledFuncsToTranslateCount = (int)state; do { - Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated"); + PtcTranslationProgressChanged?.Invoke(_translateCount, profiledFuncsToTranslateCount); } - while (!_loggerEvent.WaitOne(refreshRate * 1000)); + while (!_loggerEvent.WaitOne(refreshRate)); Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated"); } diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 15f757c87a..06be6e0579 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -79,6 +79,26 @@ namespace Ryujinx.Graphics.Gpu /// internal Capabilities Capabilities => _caps.Value; + /// + /// Signaled when shader cache begins and ends loading. + /// Signals true when loading has started, false when ended. + /// + public event Action ShaderCacheStateChanged + { + add => Methods.ShaderCache.ShaderCacheStateChanged += value; + remove => Methods.ShaderCache.ShaderCacheStateChanged -= value; + } + + /// + /// Signaled while shader cache is loading to indicate current progress. + /// Provides current and total number of shaders loaded. + /// + public event Action ShaderCacheProgressChanged + { + add => Methods.ShaderCache.ShaderCacheProgressChanged += value; + remove => Methods.ShaderCache.ShaderCacheProgressChanged -= value; + } + /// /// Creates a new instance of the GPU emulation context. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 6a3971df27..bf89f29d6f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -9,6 +9,7 @@ using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; namespace Ryujinx.Graphics.Gpu.Shader { @@ -36,6 +37,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// private const ulong ShaderCodeGenVersion = 1961; + // Progress reporting helpers + private int _shaderCount; + private readonly AutoResetEvent _progressReportEvent; + public event Action ShaderCacheStateChanged; + public event Action ShaderCacheProgressChanged; + /// /// Creates a new instance of the shader cache. /// @@ -50,6 +57,8 @@ namespace Ryujinx.Graphics.Gpu.Shader _gpPrograms = new Dictionary>(); _gpProgramsDiskCache = new Dictionary(); _cpProgramsDiskCache = new Dictionary(); + + _progressReportEvent = new AutoResetEvent(false); } /// @@ -76,12 +85,16 @@ namespace Ryujinx.Graphics.Gpu.Shader ReadOnlySpan guestProgramList = _cacheManager.GetGuestProgramList(); + _progressReportEvent.Reset(); + _shaderCount = 0; + + ShaderCacheStateChanged?.Invoke(true); + ThreadPool.QueueUserWorkItem(ProgressLogger, guestProgramList.Length); + for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++) { Hash128 key = guestProgramList[programIndex]; - Logger.Info?.Print(LogClass.Gpu, $"Compiling shader {key} ({programIndex + 1} / {guestProgramList.Length})"); - byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key); bool hasHostCache = hostProgramBinary != null; @@ -304,6 +317,8 @@ namespace Ryujinx.Graphics.Gpu.Shader _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); } + + _shaderCount = programIndex; } if (!isReadOnly) @@ -314,10 +329,28 @@ namespace Ryujinx.Graphics.Gpu.Shader _cacheManager.Synchronize(); } - Logger.Info?.Print(LogClass.Gpu, "Shader cache loaded."); + _progressReportEvent.Set(); + ShaderCacheStateChanged?.Invoke(false); + + Logger.Info?.Print(LogClass.Gpu, $"Shader cache loaded {_shaderCount} entries."); } } + /// + /// Raises ShaderCacheProgressChanged events periodically. + /// + private void ProgressLogger(object state) + { + const int refreshRate = 100; // ms + + int totalCount = (int)state; + do + { + ShaderCacheProgressChanged?.Invoke(_shaderCount, totalCount); + } + while (!_progressReportEvent.WaitOne(refreshRate)); + } + /// /// Gets a compute shader from the cache. /// @@ -787,6 +820,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } } + _progressReportEvent?.Dispose(); _cacheManager?.Dispose(); } } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 809b693b9d..7d48422c12 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -92,10 +92,14 @@ namespace Ryujinx.Ui [GUI] Label _gpuName; [GUI] Label _progressLabel; [GUI] Label _firmwareVersionLabel; - [GUI] LevelBar _progressBar; + [GUI] ProgressBar _progressBar; [GUI] Box _viewBox; [GUI] Label _vSyncStatus; [GUI] Box _listStatusBox; + [GUI] Label _loadingStatusLabel; + [GUI] ProgressBar _loadingStatusBar; + + private string _loadingStatusTitle = ""; #pragma warning restore CS0649, IDE0044, CS0169 @@ -333,6 +337,48 @@ namespace Ryujinx.Ui _emulationContext.Initialize(); } + private void SetupProgressUiHandlers() + { + Ptc.PtcTranslationStateChanged -= PtcStatusChanged; + Ptc.PtcTranslationStateChanged += PtcStatusChanged; + + Ptc.PtcTranslationProgressChanged -= LoadingProgressChanged; + Ptc.PtcTranslationProgressChanged += LoadingProgressChanged; + + _emulationContext.Gpu.ShaderCacheStateChanged -= ShaderCacheStatusChanged; + _emulationContext.Gpu.ShaderCacheStateChanged += ShaderCacheStatusChanged; + + _emulationContext.Gpu.ShaderCacheProgressChanged -= LoadingProgressChanged; + _emulationContext.Gpu.ShaderCacheProgressChanged += LoadingProgressChanged; + } + + private void ShaderCacheStatusChanged(bool state) + { + _loadingStatusTitle = "Shaders"; + Application.Invoke(delegate + { + _loadingStatusBar.Visible = _loadingStatusLabel.Visible = state; + }); + } + + private void PtcStatusChanged(bool state) + { + _loadingStatusTitle = "PTC"; + Application.Invoke(delegate + { + _loadingStatusBar.Visible = _loadingStatusLabel.Visible = state; + }); + } + + private void LoadingProgressChanged(int value, int total) + { + Application.Invoke(delegate + { + _loadingStatusBar.Fraction = (double)value / total; + _loadingStatusLabel.Text = $"{_loadingStatusTitle} : {value}/{total}"; + }); + } + public void UpdateGameTable() { if (_updatingGameTable || _gameLoaded) @@ -531,6 +577,8 @@ namespace Ryujinx.Ui _deviceExitStatus.Reset(); + SetupProgressUiHandlers(); + Translator.IsReadyForTranslation.Reset(); #if MACOS_BUILD CreateGameWindow(); @@ -775,7 +823,7 @@ namespace Ryujinx.Ui barValue = (float)args.NumAppsLoaded / args.NumAppsFound; } - _progressBar.Value = barValue; + _progressBar.Fraction = barValue; // Reset the vertical scrollbar to the top when titles finish loading if (args.NumAppsLoaded == args.NumAppsFound) diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 688f0e2553..5558403bac 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -459,13 +459,14 @@ - + 200 True False start 10 5 + 6 True @@ -640,6 +641,7 @@ False start 5 + 5 True @@ -654,16 +656,34 @@ 1 - - - True - False - - - False - True - 3 - + + + False + 5 + 5 + 0/0 + False + + + False + True + 11 + + + + + 200 + False + 5 + 5 + 6 + False + + + False + True + 12 +