Merge pull request #2 from TheRooferDev/vrr-cap-clean
VRR: optional per-game FPS cap
This commit is contained in:
@@ -13,10 +13,10 @@ namespace Ryujinx.Common.Configuration
|
|||||||
public static VSyncMode Next(this VSyncMode vsync, bool customEnabled = false) =>
|
public static VSyncMode Next(this VSyncMode vsync, bool customEnabled = false) =>
|
||||||
vsync switch
|
vsync switch
|
||||||
{
|
{
|
||||||
VSyncMode.Switch => customEnabled ? VSyncMode.Custom : VSyncMode.Unbounded,
|
VSyncMode.Switch => VSyncMode.Unbounded,
|
||||||
VSyncMode.Custom => VSyncMode.Unbounded,
|
|
||||||
VSyncMode.Unbounded => VSyncMode.Vrr,
|
VSyncMode.Unbounded => VSyncMode.Vrr,
|
||||||
VSyncMode.Vrr => VSyncMode.Switch,
|
VSyncMode.Vrr => VSyncMode.Switch,
|
||||||
|
VSyncMode.Custom => VSyncMode.Switch, // legacy mode, no longer in the cycle
|
||||||
_ => VSyncMode.Switch
|
_ => VSyncMode.Switch
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -372,7 +372,25 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
if (acquireStatus == Status.Success)
|
if (acquireStatus == Status.Success)
|
||||||
{
|
{
|
||||||
if (_device.VSyncMode == VSyncMode.Unbounded)
|
bool vrrCapped = _device.VSyncMode == VSyncMode.Vrr
|
||||||
|
&& _device.CustomVSyncIntervalEnabled
|
||||||
|
&& _device.CustomVSyncInterval > 0;
|
||||||
|
|
||||||
|
if (vrrCapped)
|
||||||
|
{
|
||||||
|
// VRR with a per-game FPS cap: force a paced interval (swap interval 1 ->
|
||||||
|
// _ticksPerFrame derived from TargetVSyncInterval) instead of running
|
||||||
|
// unbounded. The present mode stays FifoRelaxed (chosen from VSyncMode.Vrr
|
||||||
|
// in Window.cs), so the display still follows the framerate, just capped.
|
||||||
|
if (_swapInterval != 1
|
||||||
|
|| _device.TargetVSyncInterval != _targetVSyncInterval
|
||||||
|
|| _vSyncMode != _device.VSyncMode)
|
||||||
|
{
|
||||||
|
UpdateSwapInterval(1);
|
||||||
|
_vSyncMode = _device.VSyncMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_device.VSyncMode == VSyncMode.Unbounded || _device.VSyncMode == VSyncMode.Vrr)
|
||||||
{
|
{
|
||||||
if (_swapInterval != 0)
|
if (_swapInterval != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ namespace Ryujinx.HLE
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly int CustomVSyncInterval;
|
internal readonly int CustomVSyncInterval;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the custom VSync interval is enabled. Also acts as the per-game VRR FPS cap toggle.
|
||||||
|
/// </summary>
|
||||||
|
internal readonly bool EnableCustomVSyncInterval;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Control the initial state of the docked mode.
|
/// Control the initial state of the docked mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -241,6 +246,7 @@ namespace Ryujinx.HLE
|
|||||||
ushort gdbStubPort,
|
ushort gdbStubPort,
|
||||||
bool debuggerSuspendOnStart,
|
bool debuggerSuspendOnStart,
|
||||||
int customVSyncInterval,
|
int customVSyncInterval,
|
||||||
|
bool enableCustomVSyncInterval = false,
|
||||||
EnabledDirtyHack[] dirtyHacks = null)
|
EnabledDirtyHack[] dirtyHacks = null)
|
||||||
{
|
{
|
||||||
MemoryConfiguration = memoryConfiguration;
|
MemoryConfiguration = memoryConfiguration;
|
||||||
@@ -248,6 +254,7 @@ namespace Ryujinx.HLE
|
|||||||
Region = region;
|
Region = region;
|
||||||
VSyncMode = vSyncMode;
|
VSyncMode = vSyncMode;
|
||||||
CustomVSyncInterval = customVSyncInterval;
|
CustomVSyncInterval = customVSyncInterval;
|
||||||
|
EnableCustomVSyncInterval = enableCustomVSyncInterval;
|
||||||
EnableDockedMode = enableDockedMode;
|
EnableDockedMode = enableDockedMode;
|
||||||
EnablePtc = enablePtc;
|
EnablePtc = enablePtc;
|
||||||
TickScalar = tickScalar;
|
TickScalar = tickScalar;
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ namespace Ryujinx.HLE
|
|||||||
|
|
||||||
VSyncMode = Configuration.VSyncMode;
|
VSyncMode = Configuration.VSyncMode;
|
||||||
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||||
|
CustomVSyncIntervalEnabled = Configuration.EnableCustomVSyncInterval;
|
||||||
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||||
@@ -147,6 +148,13 @@ namespace Ryujinx.HLE
|
|||||||
case VSyncMode.Unbounded:
|
case VSyncMode.Unbounded:
|
||||||
TargetVSyncInterval = 1;
|
TargetVSyncInterval = 1;
|
||||||
break;
|
break;
|
||||||
|
case VSyncMode.Vrr:
|
||||||
|
// VRR runs unbounded by default (interval 1). If the per-game FPS cap is
|
||||||
|
// enabled, pace to that value instead (e.g. TOTK capped at 60).
|
||||||
|
TargetVSyncInterval = (CustomVSyncIntervalEnabled && CustomVSyncInterval > 0)
|
||||||
|
? CustomVSyncInterval
|
||||||
|
: 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -987,6 +987,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Debug.GdbStubPort,
|
Debug.GdbStubPort,
|
||||||
Debug.DebuggerSuspendOnStart,
|
Debug.DebuggerSuspendOnStart,
|
||||||
Graphics.CustomVSyncInterval,
|
Graphics.CustomVSyncInterval,
|
||||||
|
Graphics.EnableCustomVSyncInterval,
|
||||||
Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null);
|
Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,6 +260,45 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Clean VSync / VRR UI (Roofer Edition) ---
|
||||||
|
// Dropdown positions: 0 = Switch, 1 = Unbounded, 2 = VRR. We translate position <-> enum
|
||||||
|
// here so we can skip the legacy "Custom" enum value (2) and still bind by SelectedIndex.
|
||||||
|
public int VSyncModeIndex
|
||||||
|
{
|
||||||
|
get => _vSyncMode switch
|
||||||
|
{
|
||||||
|
VSyncMode.Unbounded => 1,
|
||||||
|
VSyncMode.Vrr => 2,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_vSyncMode = value switch
|
||||||
|
{
|
||||||
|
1 => VSyncMode.Unbounded,
|
||||||
|
2 => VSyncMode.Vrr,
|
||||||
|
_ => VSyncMode.Switch,
|
||||||
|
};
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(ShowVrrCapPicker));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The VRR FPS cap picker is only relevant when VRR is the selected mode.
|
||||||
|
public bool ShowVrrCapPicker => _vSyncMode == VSyncMode.Vrr;
|
||||||
|
|
||||||
|
// 0 = Off (unbounded VRR), 1 = 30, 2 = 60, 3 = 120.
|
||||||
|
public int VrrFpsCapIndex
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool EnablePptc { get; set; }
|
public bool EnablePptc { get; set; }
|
||||||
public bool EnableLowPowerPptc { get; set; }
|
public bool EnableLowPowerPptc { get; set; }
|
||||||
|
|
||||||
@@ -743,9 +782,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
MatchSystemTime = config.System.MatchSystemTime;
|
MatchSystemTime = config.System.MatchSystemTime;
|
||||||
|
|
||||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
|
||||||
CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
|
CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
|
||||||
VSyncMode = config.Graphics.VSyncMode;
|
|
||||||
|
// VSync / VRR (Roofer clean UI): set the backing field directly so VRR loads (the
|
||||||
|
// legacy VSyncMode setter rejects it), and derive the VRR cap picker from the stored
|
||||||
|
// custom-interval values.
|
||||||
|
_vSyncMode = config.Graphics.VSyncMode.Value;
|
||||||
|
VrrFpsCapIndex = config.Graphics.EnableCustomVSyncInterval.Value
|
||||||
|
? config.Graphics.CustomVSyncInterval.Value switch
|
||||||
|
{
|
||||||
|
30 => 1,
|
||||||
|
60 => 2,
|
||||||
|
120 => 3,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
: 0;
|
||||||
|
OnPropertyChanged(nameof(VSyncModeIndex));
|
||||||
|
OnPropertyChanged(nameof(ShowVrrCapPicker));
|
||||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||||
DramSize = config.System.DramSize;
|
DramSize = config.System.DramSize;
|
||||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||||
@@ -879,9 +932,22 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.System.TickScalar.Value = TurboMultiplier;
|
config.System.TickScalar.Value = TurboMultiplier;
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
config.Graphics.VSyncMode.Value = VSyncMode;
|
config.Graphics.VSyncMode.Value = _vSyncMode;
|
||||||
config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval;
|
|
||||||
config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval;
|
// VRR FPS cap -> reuse the custom-interval plumbing the engine already reads
|
||||||
|
// (EnableCustomVSyncInterval = cap on, CustomVSyncInterval = the capped fps).
|
||||||
|
bool vrrCapOn = _vSyncMode == VSyncMode.Vrr && VrrFpsCapIndex > 0;
|
||||||
|
config.Graphics.EnableCustomVSyncInterval.Value = vrrCapOn;
|
||||||
|
if (vrrCapOn)
|
||||||
|
{
|
||||||
|
config.Graphics.CustomVSyncInterval.Value = VrrFpsCapIndex switch
|
||||||
|
{
|
||||||
|
1 => 30,
|
||||||
|
2 => 60,
|
||||||
|
3 => 120,
|
||||||
|
_ => 60,
|
||||||
|
};
|
||||||
|
}
|
||||||
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
|
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
|
||||||
config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
|
config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
|
||||||
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
|
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
|
||||||
|
|||||||
@@ -178,21 +178,7 @@
|
|||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||||
Width="250" />
|
Width="250" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
IsVisible="{Binding EnableCustomVSyncInterval}"
|
SelectedIndex="{Binding VSyncModeIndex}"
|
||||||
SelectedIndex="{Binding VSyncMode}"
|
|
||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltipCustom}"
|
|
||||||
HorizontalContentAlignment="Left"
|
|
||||||
Width="350">
|
|
||||||
<ComboBoxItem
|
|
||||||
Content="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
|
||||||
<ComboBoxItem
|
|
||||||
Content="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
|
||||||
<ComboBoxItem
|
|
||||||
Content="{ext:Locale SettingsTabSystemVSyncModeCustom}" />
|
|
||||||
</ComboBox>
|
|
||||||
<ComboBox
|
|
||||||
IsVisible="{Binding !EnableCustomVSyncInterval}"
|
|
||||||
SelectedIndex="{Binding VSyncMode}"
|
|
||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||||
HorizontalContentAlignment="Left"
|
HorizontalContentAlignment="Left"
|
||||||
Width="350">
|
Width="350">
|
||||||
@@ -200,32 +186,26 @@
|
|||||||
Content="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
Content="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
||||||
<ComboBoxItem
|
<ComboBoxItem
|
||||||
Content="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
Content="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
||||||
|
<ComboBoxItem
|
||||||
|
Content="VRR" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel IsVisible="{Binding EnableCustomVSyncInterval}"
|
<StackPanel IsVisible="{Binding ShowVrrCapPicker}"
|
||||||
Margin="0,0,0,10"
|
Margin="0,0,0,10"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{ext:Locale SettingsTabSystemCustomVSyncIntervalPercentage}"
|
Text="Plafond VRR (FPS)"
|
||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalValueTooltip}"
|
|
||||||
Width="250" />
|
Width="250" />
|
||||||
<Slider Value="{Binding CustomVSyncIntervalPercentageProxy}"
|
<ComboBox
|
||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalSliderTooltip}"
|
SelectedIndex="{Binding VrrFpsCapIndex}"
|
||||||
MinWidth="175"
|
HorizontalContentAlignment="Left"
|
||||||
Margin="10,-3,0,0"
|
Width="350">
|
||||||
Height="32"
|
<ComboBoxItem Content="Off" />
|
||||||
Padding="0,-5"
|
<ComboBoxItem Content="30" />
|
||||||
TickFrequency="1"
|
<ComboBoxItem Content="60" />
|
||||||
IsSnapToTickEnabled="True"
|
<ComboBoxItem Content="120" />
|
||||||
LargeChange="10"
|
</ComboBox>
|
||||||
SmallChange="1"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Minimum="10"
|
|
||||||
Maximum="400" />
|
|
||||||
<TextBlock Margin="5,0"
|
|
||||||
Width="40"
|
|
||||||
Text="{Binding CustomVSyncIntervalPercentageText}"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -282,11 +262,6 @@
|
|||||||
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
|
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
|
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<CheckBox
|
|
||||||
IsChecked="{Binding EnableCustomVSyncInterval}"
|
|
||||||
ToolTip.Tip="{ext:Locale SettingsTabSystemEnableCustomVSyncIntervalTooltip}">
|
|
||||||
<TextBlock Text="{ext:Locale SettingsTabSystemEnableCustomVSyncInterval}" />
|
|
||||||
</CheckBox>
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
IsChecked="{Binding SkipUserProfiles}"
|
IsChecked="{Binding SkipUserProfiles}"
|
||||||
ToolTip.Tip="{ext:Locale SkipUserProfilesTooltip}">
|
ToolTip.Tip="{ext:Locale SkipUserProfilesTooltip}">
|
||||||
|
|||||||
Reference in New Issue
Block a user