diff --git a/src/Ryujinx.Common/ReactiveObject.cs b/src/Ryujinx.Common/ReactiveObject.cs index 4831edb52..8d50acf5a 100644 --- a/src/Ryujinx.Common/ReactiveObject.cs +++ b/src/Ryujinx.Common/ReactiveObject.cs @@ -41,10 +41,12 @@ namespace Ryujinx.Common } } - public static implicit operator T(ReactiveObject obj) - { - return obj.Value; - } + public static implicit operator T(ReactiveObject obj) => obj.Value; + } + + public static class ReactiveObjectHelper + { + public static void Toggle(this ReactiveObject rBoolean) => rBoolean.Value = !rBoolean.Value; } public class ReactiveEventArgs diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index 85d0bd729..9b5e2cc42 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.GAL IPipeline Pipeline { get; } IWindow Window { get; } + + uint ProgramCount { get; } void BackgroundContextAction(Action action, bool alwaysBackground = false); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 05e57bfdf..6375d290c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -55,6 +55,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _refProducerPtr; private int _refConsumerPtr; + public uint ProgramCount { get; set; } = 0; + private Action _interruptAction; private readonly Lock _interruptLock = new(); @@ -307,6 +309,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading Programs.Add(request); + ProgramCount++; + New().Set(Ref((IProgramRequest)request)); QueueCommand(); diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 9fcdf1ad7..c241379c4 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -29,6 +29,8 @@ namespace Ryujinx.Graphics.OpenGL private readonly Sync _sync; + public uint ProgramCount { get; set; } = 0; + public event EventHandler ScreenCaptured; internal PersistentBuffers PersistentBuffers { get; } @@ -94,6 +96,8 @@ namespace Ryujinx.Graphics.OpenGL public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { + ProgramCount++; + return new Program(shaders, info.FragmentOutputMap); } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 9e51e781a..5416393f6 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _initialized; + public uint ProgramCount { get; set; } = 0; + internal FormatCapabilities FormatCapabilities { get; private set; } internal HardwareCapabilities Capabilities; @@ -511,6 +513,8 @@ namespace Ryujinx.Graphics.Vulkan public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) { + ProgramCount++; + bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute; if (info.State.HasValue || isCompute) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index e9e919b55..400831cfa 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -104,6 +104,10 @@ namespace Ryujinx.Ava private CursorStates _cursorState = !ConfigurationState.Instance.Hid.EnableMouse.Value ? CursorStates.CursorIsVisible : CursorStates.CursorIsHidden; + private DateTime _lastShaderReset; + private uint _displayCount; + private uint _previousCount = 0; + private bool _isStopped; private bool _isActive; private bool _renderingStarted; @@ -121,7 +125,6 @@ namespace Ryujinx.Ava private readonly Lock _lockObject = new(); public event EventHandler AppExit; - public event EventHandler StatusInitEvent; public event EventHandler StatusUpdatedEvent; public VirtualFileSystem VirtualFileSystem { get; } @@ -571,8 +574,7 @@ namespace Ryujinx.Ava } _isStopped = true; - _isActive = false; - DiscordIntegrationModule.SwitchToMainState(); + Stop(); } public void DisposeContext() @@ -1107,14 +1109,14 @@ namespace Ryujinx.Ava public void InitStatus() { - StatusInitEvent?.Invoke(this, new StatusInitEventArgs( - ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch - { - GraphicsBackend.Vulkan => "Vulkan", - GraphicsBackend.OpenGl => "OpenGL", - _ => throw new NotImplementedException() - }, - $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); + _viewModel.BackendText = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch + { + GraphicsBackend.Vulkan => "Vulkan", + GraphicsBackend.OpenGl => "OpenGL", + _ => throw new NotImplementedException() + }; + + _viewModel.GpuNameText = $"GPU: {_renderer.GetHardwareInfo().GpuDriver}"; } public void UpdateStatus() @@ -1123,6 +1125,8 @@ namespace Ryujinx.Ava string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; string vSyncMode = Device.VSyncMode.ToString(); + UpdateShaderCount(); + if (GraphicsConfig.ResScale != 1) { dockedMode += $" ({GraphicsConfig.ResScale}x)"; @@ -1134,7 +1138,8 @@ namespace Ryujinx.Ava dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %")); + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", + _displayCount)); } public async Task ShowExitPrompt() @@ -1160,6 +1165,24 @@ namespace Ryujinx.Ava } } + private void UpdateShaderCount() + { + // If there is a mismatch between total program compile and previous count + // this means new shaders have been compiled and should be displayed. + if (_renderer.ProgramCount != _previousCount) + { + _displayCount += _renderer.ProgramCount - _previousCount; + _lastShaderReset = DateTime.Now; + _previousCount = _renderer.ProgramCount; + } + // Check if 5s has passed since any new shaders were compiled. + // If yes, reset the counter. + else if (_lastShaderReset.AddSeconds(5) <= DateTime.Now) + { + _displayCount = 0; + } + } + private bool UpdateFrame() { if (!_isActive) diff --git a/src/Ryujinx/UI/Models/StatusUpdatedEventArgs.cs b/src/Ryujinx/UI/Models/StatusUpdatedEventArgs.cs index 7e68841b6..12841c3d1 100644 --- a/src/Ryujinx/UI/Models/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx/UI/Models/StatusUpdatedEventArgs.cs @@ -10,8 +10,10 @@ namespace Ryujinx.Ava.UI.Models public string DockedMode { get; } public string FifoStatus { get; } public string GameStatus { get; } + + public uint ShaderCount { get; } - public StatusUpdatedEventArgs(string vSyncMode, string volumeStatus, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus) + public StatusUpdatedEventArgs(string vSyncMode, string volumeStatus, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, uint shaderCount) { VSyncMode = vSyncMode; VolumeStatus = volumeStatus; @@ -19,6 +21,7 @@ namespace Ryujinx.Ava.UI.Models AspectRatio = aspectRatio; GameStatus = gameStatus; FifoStatus = fifoStatus; + ShaderCount = shaderCount; } } } diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 6bcafb6d5..ab3f8db62 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -9,6 +9,7 @@ using DynamicData; using DynamicData.Alias; using DynamicData.Binding; using FluentAvalonia.UI.Controls; +using Gommon; using LibHac.Common; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; @@ -69,7 +70,9 @@ namespace Ryujinx.Ava.UI.ViewModels private string _gameStatusText; private string _volumeStatusText; private string _gpuStatusText; + private string _shaderCountText; private bool _isAmiiboRequested; + private bool _showRightmostSeparator; private bool _isAmiiboBinRequested; private bool _isGameRunning; private bool _isFullScreen; @@ -324,6 +327,17 @@ namespace Ryujinx.Ava.UI.ViewModels public bool ShowFirmwareStatus => !ShowLoadProgress; + public bool ShowRightmostSeparator + { + get => _showRightmostSeparator; + set + { + _showRightmostSeparator = value; + + OnPropertyChanged(); + } + } + public bool IsGameRunning { get => _isGameRunning; @@ -680,6 +694,16 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } + + public string ShaderCountText + { + get => _shaderCountText; + set + { + _shaderCountText = value; + OnPropertyChanged(); + } + } public string BackendText { @@ -1473,8 +1497,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void InitializeGame() { RendererHostControl.WindowCreated += RendererHost_Created; - - AppHost.StatusInitEvent += Init_StatusBar; + AppHost.StatusUpdatedEvent += Update_StatusBar; AppHost.AppExit += AppHost_AppExit; @@ -1501,18 +1524,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - private void Init_StatusBar(object sender, StatusInitEventArgs args) - { - if (ShowMenuAndStatusBar && !ShowLoadProgress) - { - Dispatcher.UIThread.InvokeAsync(() => - { - GpuNameText = args.GpuName; - BackendText = args.GpuBackend; - }); - } - } - private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) { if (ShowMenuAndStatusBar && !ShowLoadProgress) @@ -1536,6 +1547,8 @@ namespace Ryujinx.Ava.UI.ViewModels GameStatusText = args.GameStatus; VolumeStatusText = args.VolumeStatus; FifoStatusText = args.FifoStatus; + ShaderCountText = args.ShaderCount > 0 ? $"Compiling shaders: {args.ShaderCount}" : string.Empty; + ShowRightmostSeparator = !ShaderCountText.IsNullOrEmpty(); ShowStatusSeparator = true; }); diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml index 462ad4219..a15c62f21 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml @@ -325,6 +325,19 @@ IsVisible="{Binding !ShowLoadProgress}" Text="{Binding GpuNameText}" TextAlignment="Start" /> + +