Last active
November 19, 2025 22:43
-
-
Save MoreOrLessSoftware/7ab6eb4881059070b23a68fc58ca9dc2 to your computer and use it in GitHub Desktop.
PowerShell script for changing the FPS limit in a RivaTuner Statistics Server profile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| param( | |
| # One or more (separated by commas) RTSS profile names to modify (e.g. Global or MyApp.exe or Global,MyApp.exe) | |
| [string[]]$profiles = ("Global"), | |
| # The FPS limit to set (use 0 for no limit, "auto" for current primary display refresh rate) | |
| $limit = "auto", | |
| # Optional limit denominator if you want to set a fractional FPS limit (e.g. for 59.56, use limit = 5956 and limitDenominator = 100) | |
| [int]$limitDenominator = 1, | |
| # Delay before attempting to read the current display refresh rate | |
| [int]$autoLimitDelaySeconds = 0 | |
| ) | |
| # RivaTunerStatisticsServer v7.3.7 or higher is required | |
| # | |
| # Example usage: | |
| # Set the global FPS limit to the current primary display refresh rate: | |
| # .\Set-RTSS-Profile-Limit.ps1 | |
| # Set the global FPS limit to 60 FPS: | |
| # .\Set-RTSS-Profile-Limit.ps1 Global 60 | |
| # Disable the global FPS limit (reset it to 0): | |
| # .\Set-RTSS-Profile-Limit.ps1 Global 0 | |
| # Set the FPS limit of the Dolphin emulator to 59.56 FPS | |
| # .\Set-RTSS-Profile-Limit.ps1 Dolphin.exe 5956 100 | |
| # RTSS interop | |
| if (-not ("RTSS" -as [type])) { | |
| Add-Type -TypeDefinition @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public static class RTSS | |
| { | |
| [DllImport("RTSSHooks64.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] | |
| public static extern bool SetProfileProperty(string name, IntPtr data, uint size); | |
| [DllImport("RTSSHooks64.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] | |
| public static extern bool GetProfileProperty(string name, IntPtr data, uint size); | |
| [DllImport("RTSSHooks64.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] | |
| public static extern void LoadProfile(string profile); | |
| [DllImport("RTSSHooks64.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] | |
| public static extern void SaveProfile(string profile); | |
| [DllImport("RTSSHooks64.dll", CallingConvention = CallingConvention.StdCall)] | |
| public static extern void UpdateProfiles(); | |
| } | |
| "@ | |
| } | |
| # Determine the current display refresh rate if "auto" mode | |
| if ("auto" -eq $limit) { | |
| if (-not (Get-Command Get-DisplayInfo -ErrorAction Ignore)) { | |
| Write-Host "Required module DisplayConfig does not appear to be installed" -ForegroundColor Red | |
| Write-Host "Installation command:" | |
| Write-Host " Install-Module -Name DisplayConfig" -ForegroundColor DarkGray | |
| Exit 1 | |
| } | |
| if ($autoLimitDelaySeconds -gt 0) { | |
| Write-Host "Waiting $autoLimitDelaySeconds seconds before getting current refresh rate..." | |
| Start-Sleep -Seconds $autoLimitDelaySeconds | |
| } | |
| $displayInfo = Get-DisplayInfo | Where-Object { $_.Primary } | |
| $limit = $displayInfo.Mode.RefreshRate | |
| Write-Host "Detected primary display refresh rate: $limit" | |
| } | |
| # Limit must be an integer | |
| try { | |
| $limit = [decimal]$limit | |
| if (($limit % 1) -ne 0) { | |
| $limit = $limit * 100 | |
| $limitDenominator = 100 | |
| } | |
| $limit = [int]$limit | |
| } | |
| catch { | |
| Write-Host "Failed to convert limit to number: $($_.Exception.Message)" -ForegroundColor Red | |
| Exit 1 | |
| } | |
| try { | |
| # IntPtr for dll calls | |
| $ptr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4) | |
| # Loop through all the profiles | |
| foreach ($profileName in $profiles) { | |
| # Normalize the profile name | |
| $profileName = $profileName -replace "(.exe)?(.cfg)?$", "" | |
| $profileNameFriendly = $profileName | |
| if ($profileName -eq "Global") { | |
| $profileName = "" | |
| } | |
| # Load the RTSS profile | |
| try { | |
| [RTSS]::LoadProfile($profileName) | |
| } catch { | |
| Write-Host "Failed to load profile '$profileName': $($_.Exception.Message)" -ForegroundColor Red | |
| continue | |
| } | |
| # Read the current limit | |
| try { | |
| $currentLimitRead = [RTSS]::GetProfileProperty("FramerateLimit", $ptr, 4) | |
| if ($currentLimitRead) { | |
| $currentLimit = [System.Runtime.InteropServices.Marshal]::ReadInt32($ptr) | |
| } | |
| } catch { | |
| Write-Host "Failed to retrieve FramerateLimit on profile '$profileNameFriendly': $($_.Exception.Message)" -ForegroundColor Red | |
| continue | |
| } | |
| # Update the framerate limit denominator | |
| [System.Runtime.InteropServices.Marshal]::WriteInt32($ptr, $limitDenominator) | |
| if (![RTSS]::SetProfileProperty("FramerateLimitDenominator", $ptr, 4)) { | |
| Write-Host "Failed to set FramerateLimitDenominator property on profile '$profileNameFriendly'" -ForegroundColor Red | |
| continue | |
| } | |
| # Update the framerate limit | |
| [System.Runtime.InteropServices.Marshal]::WriteInt32($ptr, $limit) | |
| if (![RTSS]::SetProfileProperty("FramerateLimit", $ptr, 4)) { | |
| Write-Host "Failed to set FramerateLimit property on profile '$profileNameFriendly'" -ForegroundColor Red | |
| continue | |
| } | |
| # Save the profile | |
| try { | |
| [RTSS]::SaveProfile($profileName) | |
| Write-Output "[Profile: $profileNameFriendly] Updated FPS limit ($currentLimit >> $limit[/$limitDenominator])" | |
| } catch { | |
| Write-Host "Failed to save profile '$profileName': $($_.Exception.Message)" -ForegroundColor Red | |
| } | |
| } | |
| } finally { | |
| # Free pointer memory | |
| [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ptr) | |
| } | |
| # Ensure the profile changes take effect | |
| try { | |
| [RTSS]::UpdateProfiles() | |
| Write-Host "✓ " -NoNewline -ForegroundColor Green | |
| Write-Host "RTSS profiles updated" | |
| } catch { | |
| Write-Host "Failed to update RTSS profiles: $($_.Exception.Message)" -ForegroundColor Red | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! this just proved super-helpful to me, setting RTSS to match refresh rates when streaming with Apollo (the Sunshine fork). It could also be done with RTSS-CLI, but in order to use fractional refresh rates, I needed to be able to set LimitDenominator as well, and I'm not sure if RTSS-CLI could handle that. This worked great!
I describe how I'm using it in a discussion thread for Apollo here.