Add custom refresh rate mode to VSync option (#238)

Rebased @jcm93's refreshinterval branch:
https://github.com/jcm93/Ryujinx/tree/refreshinterval

The option is placed under System/Hacks. Disabled, it's the default
Ryujinx behavior. Enabled, the behavior is shown in the attached
screenshots. If a framerate is too high or low, you can adjust the value
where you normally toggle VSync on and off. It will also cycle through
the default on/off toggles.

Also, in order to reduce clutter, I made an adjustment to remove the
target FPS and only show the percentage.

---------

Co-authored-by: jcm <6864788+jcm93@users.noreply.github.com>
This commit is contained in:
Keaton 2024-11-25 13:39:09 -06:00 committed by GitHub
parent 7e16fccfc1
commit 2e6794e69b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 678 additions and 110 deletions

View file

@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.UI;
using System;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE
{
@ -84,9 +85,14 @@ namespace Ryujinx.HLE
internal readonly RegionCode Region;
/// <summary>
/// Control the initial state of the vertical sync in the SurfaceFlinger service.
/// Control the initial state of the present interval in the SurfaceFlinger service (previously Vsync).
/// </summary>
internal readonly bool EnableVsync;
internal readonly VSyncMode VSyncMode;
/// <summary>
/// Control the custom VSync interval, if enabled and active.
/// </summary>
internal readonly int CustomVSyncInterval;
/// <summary>
/// Control the initial state of the docked mode.
@ -195,7 +201,7 @@ namespace Ryujinx.HLE
IHostUIHandler hostUIHandler,
SystemLanguage systemLanguage,
RegionCode region,
bool enableVsync,
VSyncMode vSyncMode,
bool enableDockedMode,
bool enablePtc,
bool enableInternetAccess,
@ -212,7 +218,8 @@ namespace Ryujinx.HLE
MultiplayerMode multiplayerMode,
bool multiplayerDisableP2p,
string multiplayerLdnPassphrase,
string multiplayerLdnServer)
string multiplayerLdnServer,
int customVSyncInterval)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
@ -225,7 +232,8 @@ namespace Ryujinx.HLE
HostUIHandler = hostUIHandler;
SystemLanguage = systemLanguage;
Region = region;
EnableVsync = enableVsync;
VSyncMode = vSyncMode;
CustomVSyncInterval = customVSyncInterval;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
EnableInternetAccess = enableInternetAccess;

View file

@ -10,13 +10,12 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
class SurfaceFlinger : IConsumerListener, IDisposable
{
private const int TargetFps = 60;
private readonly Switch _device;
private readonly Dictionary<long, Layer> _layers;
@ -32,6 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private readonly long _spinTicks;
private readonly long _1msTicks;
private VSyncMode _vSyncMode;
private long _targetVSyncInterval;
private int _swapInterval;
private int _swapIntervalDelay;
@ -88,7 +90,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
else
{
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval;
_targetVSyncInterval = _device.TargetVSyncInterval;
}
}
@ -370,15 +373,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
if (acquireStatus == Status.Success)
{
// If device vsync is disabled, reflect the change.
if (!_device.EnableDeviceVsync)
if (_device.VSyncMode == VSyncMode.Unbounded)
{
if (_swapInterval != 0)
{
UpdateSwapInterval(0);
_vSyncMode = _device.VSyncMode;
}
}
else if (item.SwapInterval != _swapInterval)
else if (_device.VSyncMode != _vSyncMode)
{
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
_vSyncMode = _device.VSyncMode;
}
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
{
UpdateSwapInterval(item.SwapInterval);
}

View file

@ -27,7 +27,11 @@ namespace Ryujinx.HLE
public TamperMachine TamperMachine { get; }
public IHostUIHandler UIHandler { get; }
public bool EnableDeviceVsync { get; set; }
public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
public bool CustomVSyncIntervalEnabled { get; set; } = false;
public int CustomVSyncInterval { get; set; }
public long TargetVSyncInterval { get; set; } = 60;
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
@ -59,12 +63,14 @@ namespace Ryujinx.HLE
System.State.SetLanguage(Configuration.SystemLanguage);
System.State.SetRegion(Configuration.Region);
EnableDeviceVsync = Configuration.EnableVsync;
VSyncMode = Configuration.VSyncMode;
CustomVSyncInterval = Configuration.CustomVSyncInterval;
System.State.DockedMode = Configuration.EnableDockedMode;
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
UpdateVSyncInterval();
#pragma warning restore IDE0055
}
@ -75,6 +81,34 @@ namespace Ryujinx.HLE
Gpu.GPFifo.DispatchCalls();
}
public void IncrementCustomVSyncInterval()
{
CustomVSyncInterval += 1;
UpdateVSyncInterval();
}
public void DecrementCustomVSyncInterval()
{
CustomVSyncInterval -= 1;
UpdateVSyncInterval();
}
public void UpdateVSyncInterval()
{
switch (VSyncMode)
{
case VSyncMode.Custom:
TargetVSyncInterval = CustomVSyncInterval;
break;
case VSyncMode.Switch:
TargetVSyncInterval = 60;
break;
case VSyncMode.Unbounded:
TargetVSyncInterval = 1;
break;
}
}
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile);