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) =>
|
||||
vsync switch
|
||||
{
|
||||
VSyncMode.Switch => customEnabled ? VSyncMode.Custom : VSyncMode.Unbounded,
|
||||
VSyncMode.Custom => VSyncMode.Unbounded,
|
||||
VSyncMode.Switch => VSyncMode.Unbounded,
|
||||
VSyncMode.Unbounded => VSyncMode.Vrr,
|
||||
VSyncMode.Vrr => VSyncMode.Switch,
|
||||
VSyncMode.Custom => VSyncMode.Switch, // legacy mode, no longer in the cycle
|
||||
_ => VSyncMode.Switch
|
||||
};
|
||||
}
|
||||
|
||||
@@ -372,7 +372,25 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -93,6 +93,11 @@ namespace Ryujinx.HLE
|
||||
/// </summary>
|
||||
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>
|
||||
/// Control the initial state of the docked mode.
|
||||
/// </summary>
|
||||
@@ -241,6 +246,7 @@ namespace Ryujinx.HLE
|
||||
ushort gdbStubPort,
|
||||
bool debuggerSuspendOnStart,
|
||||
int customVSyncInterval,
|
||||
bool enableCustomVSyncInterval = false,
|
||||
EnabledDirtyHack[] dirtyHacks = null)
|
||||
{
|
||||
MemoryConfiguration = memoryConfiguration;
|
||||
@@ -248,6 +254,7 @@ namespace Ryujinx.HLE
|
||||
Region = region;
|
||||
VSyncMode = vSyncMode;
|
||||
CustomVSyncInterval = customVSyncInterval;
|
||||
EnableCustomVSyncInterval = enableCustomVSyncInterval;
|
||||
EnableDockedMode = enableDockedMode;
|
||||
EnablePtc = enablePtc;
|
||||
TickScalar = tickScalar;
|
||||
|
||||
@@ -98,6 +98,7 @@ namespace Ryujinx.HLE
|
||||
|
||||
VSyncMode = Configuration.VSyncMode;
|
||||
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||
CustomVSyncIntervalEnabled = Configuration.EnableCustomVSyncInterval;
|
||||
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||
@@ -147,6 +148,13 @@ namespace Ryujinx.HLE
|
||||
case VSyncMode.Unbounded:
|
||||
TargetVSyncInterval = 1;
|
||||
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.DebuggerSuspendOnStart,
|
||||
Graphics.CustomVSyncInterval,
|
||||
Graphics.EnableCustomVSyncInterval,
|
||||
Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,45 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
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 EnableLowPowerPptc { get; set; }
|
||||
|
||||
@@ -743,9 +782,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
MatchSystemTime = config.System.MatchSystemTime;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
||||
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;
|
||||
DramSize = config.System.DramSize;
|
||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||
@@ -879,9 +932,22 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.System.TickScalar.Value = TurboMultiplier;
|
||||
|
||||
// Graphics
|
||||
config.Graphics.VSyncMode.Value = VSyncMode;
|
||||
config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval;
|
||||
config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval;
|
||||
config.Graphics.VSyncMode.Value = _vSyncMode;
|
||||
|
||||
// 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.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
|
||||
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
|
||||
|
||||
@@ -178,21 +178,7 @@
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox
|
||||
IsVisible="{Binding EnableCustomVSyncInterval}"
|
||||
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}"
|
||||
SelectedIndex="{Binding VSyncModeIndex}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
@@ -200,32 +186,26 @@
|
||||
Content="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
||||
<ComboBoxItem
|
||||
Content="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
||||
<ComboBoxItem
|
||||
Content="VRR" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel IsVisible="{Binding EnableCustomVSyncInterval}"
|
||||
<StackPanel IsVisible="{Binding ShowVrrCapPicker}"
|
||||
Margin="0,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabSystemCustomVSyncIntervalPercentage}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalValueTooltip}"
|
||||
Text="Plafond VRR (FPS)"
|
||||
Width="250" />
|
||||
<Slider Value="{Binding CustomVSyncIntervalPercentageProxy}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalSliderTooltip}"
|
||||
MinWidth="175"
|
||||
Margin="10,-3,0,0"
|
||||
Height="32"
|
||||
Padding="0,-5"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
LargeChange="10"
|
||||
SmallChange="1"
|
||||
VerticalAlignment="Center"
|
||||
Minimum="10"
|
||||
Maximum="400" />
|
||||
<TextBlock Margin="5,0"
|
||||
Width="40"
|
||||
Text="{Binding CustomVSyncIntervalPercentageText}"/>
|
||||
<ComboBox
|
||||
SelectedIndex="{Binding VrrFpsCapIndex}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem Content="Off" />
|
||||
<ComboBoxItem Content="30" />
|
||||
<ComboBoxItem Content="60" />
|
||||
<ComboBoxItem Content="120" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
||||
<TextBlock
|
||||
@@ -282,11 +262,6 @@
|
||||
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
IsChecked="{Binding EnableCustomVSyncInterval}"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabSystemEnableCustomVSyncIntervalTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemEnableCustomVSyncInterval}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
IsChecked="{Binding SkipUserProfiles}"
|
||||
ToolTip.Tip="{ext:Locale SkipUserProfilesTooltip}">
|
||||
|
||||
Reference in New Issue
Block a user