From 41385918cc15eca12c295ecadace323782a16f98 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Sun, 9 Nov 2025 22:53:27 -0500 Subject: [PATCH 01/11] Add rudimentary reflex implementation --- .../LowLatency/NVAPILowLatencyProvider.cs | 59 +++++++++++++++++++ osu.Desktop/NVAPI.cs | 53 +++++++++++++++++ osu.Desktop/Program.cs | 4 ++ 3 files changed, 116 insertions(+) create mode 100644 osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs new file mode 100644 index 000000000000..269a5574a311 --- /dev/null +++ b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#pragma warning disable IDE1006 // Naming rule violation + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; +using osu.Framework.Graphics.Rendering.LowLatency; + +namespace osu.Desktop.LowLatency +{ + /// + /// Provider for NVIDIA's NVAPI (Reflex) low latency features. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SupportedOSPlatform("windows")] + internal sealed class NVAPILowLatencyProvider : ILowLatencyProvider + { + public bool IsAvailable { get; private set; } + + private IntPtr _deviceHandle; + + public void Initialize(IntPtr nativeDeviceHandle) + { + _deviceHandle = nativeDeviceHandle; + IsAvailable = NVAPI.Available && _deviceHandle != IntPtr.Zero; + } + + public void SetMode(LatencyMode mode) + { + if (!IsAvailable || _deviceHandle == IntPtr.Zero) + return; + + bool enable = mode != LatencyMode.Off; + bool boost = mode == LatencyMode.Boost; + var status = NVAPI.SetSleepModeHelper(_deviceHandle, enable, boost, boost, 1000); + Console.WriteLine("NVAPI SetSleepMode status: " + status); + } + + public void SetMarker(LatencyMarker marker, ulong frameId) + { + if (!IsAvailable || _deviceHandle == IntPtr.Zero) + return; + + var status = NVAPI.SetLatencyMarkerHelper(_deviceHandle, (uint)marker, frameId); + Console.WriteLine("NVAPI SetMarker status: " + status); + } + + public void FrameSleep() + { + if (!IsAvailable || _deviceHandle == IntPtr.Zero) + return; + + var status = NVAPI.FrameSleepHelper(_deviceHandle); + Console.WriteLine("NVAPI FrameSleep status: " + status); + } + } +} diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index fd372cddc582..b65699b5cc34 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -104,6 +104,21 @@ internal static class NVAPI private static readonly SaveSettingsDelegate SaveSettings; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus SetSleepModeDelegate(IntPtr pDevice, ref NvSetSleepModeParams pParams); + + private static readonly SetSleepModeDelegate SetSleepMode; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus SetLatencyMarkerDelegate(IntPtr pDevice, uint marker, ulong frameId); + + private static readonly SetLatencyMarkerDelegate SetLatencyMarker; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus SleepDelegate(IntPtr pDevice); + + private static readonly SleepDelegate Sleep; + public static NvStatus Status { get; private set; } = NvStatus.OK; public static bool Available { get; private set; } @@ -338,6 +353,27 @@ private static bool setSetting(NvSettingID settingId, uint settingValue) return !checkError(SaveSettings(sessionHandle), nameof(SaveSettings)); } + internal static NvStatus SetSleepModeHelper(IntPtr devicePtr, bool enable, bool boost, bool useMarkersOptimize, uint minimumIntervalUs) + { + if (!Available) + return NvStatus.NO_IMPLEMENTATION; + + NvSetSleepModeParams sleepParams = new NvSetSleepModeParams + { + version = NvSetSleepModeParams.Stride, + bLowLatencyMode = enable ? 1u : 0u, + bLowLatencyBoost = boost ? 1u : 0u, + bUseMarkersToOptimize = useMarkersOptimize ? 1u : 0u, + minimumIntervalUs = minimumIntervalUs, + }; + + return SetSleepMode(devicePtr, ref sleepParams); + } + + internal static NvStatus SetLatencyMarkerHelper(IntPtr devicePtr, uint marker, ulong frameId) => SetLatencyMarker(devicePtr, marker, frameId); + + internal static NvStatus FrameSleepHelper(IntPtr devicePtr) => Sleep(devicePtr); + /// /// Creates a session to access the driver configuration. /// @@ -400,6 +436,9 @@ static NVAPI() getDelegate(0x7FA2173A, out EnumApplications); getDelegate(0x4347A9DE, out CreateApplication); getDelegate(0xFCBC7E14, out SaveSettings); + getDelegate(0xAC1CA9E0, out SetSleepMode); + getDelegate(0xD9984C05, out SetLatencyMarker); + getDelegate(0x852CD1D2, out Sleep); } if (createSession()) @@ -426,6 +465,20 @@ private static void getDelegate(uint id, out T newDelegate) where T : class private delegate NvStatus InitializeDelegate(); } + [StructLayout(LayoutKind.Sequential)] + internal struct NvSetSleepModeParams + { + public uint version; + public uint bLowLatencyMode; + public uint bLowLatencyBoost; + public uint bUseMarkersToOptimize; + public uint minimumIntervalUs; + public uint reserved0; + public uint reserved1; + public uint reserved2; + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvSetSleepModeParams)) | (1 << 16); + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct NvSetting { diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 612edb24706d..4ccb0f170a4f 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -5,9 +5,11 @@ using System.IO; using System.Runtime.Versioning; using osu.Desktop.LegacyIpc; +using osu.Desktop.LowLatency; using osu.Desktop.Windows; using osu.Framework; using osu.Framework.Development; +using osu.Framework.Graphics.Rendering.LowLatency; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game; @@ -138,6 +140,8 @@ public static void Main(string[] args) host.Run(new TournamentGame()); else { + host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); + host.SetLowLatencyMode(LatencyMode.On); host.Run(new OsuGameDesktop(args) { IsFirstRun = isFirstRun From 934e18e99776854acf8db98c2479ac97eb97327c Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Mon, 10 Nov 2025 07:03:23 -0500 Subject: [PATCH 02/11] fix things up --- .../LowLatency/NVAPILowLatencyProvider.cs | 3 +- osu.Desktop/NVAPI.cs | 59 ++++++++++++++----- osu.Desktop/Program.cs | 2 - .../Sections/Graphics/RendererSettings.cs | 12 ++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs index 269a5574a311..989fc2c0c2e9 100644 --- a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs +++ b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs @@ -35,6 +35,7 @@ public void SetMode(LatencyMode mode) bool enable = mode != LatencyMode.Off; bool boost = mode == LatencyMode.Boost; var status = NVAPI.SetSleepModeHelper(_deviceHandle, enable, boost, boost, 1000); + Console.WriteLine("NVAPI SetSleepMode status: " + status); } @@ -44,7 +45,6 @@ public void SetMarker(LatencyMarker marker, ulong frameId) return; var status = NVAPI.SetLatencyMarkerHelper(_deviceHandle, (uint)marker, frameId); - Console.WriteLine("NVAPI SetMarker status: " + status); } public void FrameSleep() @@ -53,7 +53,6 @@ public void FrameSleep() return; var status = NVAPI.FrameSleepHelper(_deviceHandle); - Console.WriteLine("NVAPI FrameSleep status: " + status); } } } diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index b65699b5cc34..79ffefc21e5d 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -110,7 +110,7 @@ internal static class NVAPI private static readonly SetSleepModeDelegate SetSleepMode; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate NvStatus SetLatencyMarkerDelegate(IntPtr pDevice, uint marker, ulong frameId); + private delegate NvStatus SetLatencyMarkerDelegate(IntPtr pDevice, ref NvFrameMarkerParams pParams); private static readonly SetLatencyMarkerDelegate SetLatencyMarker; @@ -361,16 +361,25 @@ internal static NvStatus SetSleepModeHelper(IntPtr devicePtr, bool enable, bool NvSetSleepModeParams sleepParams = new NvSetSleepModeParams { version = NvSetSleepModeParams.Stride, - bLowLatencyMode = enable ? 1u : 0u, - bLowLatencyBoost = boost ? 1u : 0u, - bUseMarkersToOptimize = useMarkersOptimize ? 1u : 0u, - minimumIntervalUs = minimumIntervalUs, + bLowLatencyMode = enable ? (byte)1 : (byte)0, + bLowLatencyBoost = boost ? (byte)1 : (byte)0, + minimumIntervalUs = 0, + bUseMarkersToOptimize = useMarkersOptimize ? (byte)1 : (byte)0, }; return SetSleepMode(devicePtr, ref sleepParams); } - internal static NvStatus SetLatencyMarkerHelper(IntPtr devicePtr, uint marker, ulong frameId) => SetLatencyMarker(devicePtr, marker, frameId); + internal static NvStatus SetLatencyMarkerHelper(IntPtr devicePtr, uint marker, ulong frameId) + { + var frameMarkerParams = new NvFrameMarkerParams + { + version = NvFrameMarkerParams.Stride, + frameID = frameId, + markerType = marker + }; + return SetLatencyMarker(devicePtr, ref frameMarkerParams); + } internal static NvStatus FrameSleepHelper(IntPtr devicePtr) => Sleep(devicePtr); @@ -465,18 +474,38 @@ private static void getDelegate(uint id, out T newDelegate) where T : class private delegate NvStatus InitializeDelegate(); } - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct NvSetSleepModeParams { public uint version; - public uint bLowLatencyMode; - public uint bLowLatencyBoost; - public uint bUseMarkersToOptimize; - public uint minimumIntervalUs; - public uint reserved0; - public uint reserved1; - public uint reserved2; - public static uint Stride => (uint)Marshal.SizeOf(typeof(NvSetSleepModeParams)) | (1 << 16); + public byte bLowLatencyMode; + public byte bLowLatencyBoost; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + private byte[] _pad0; // explicit padding to align next uint + + public uint minimumIntervalUs; // NvU32 + + public byte bUseMarkersToOptimize; // NvBool (1 byte) + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] + private byte[] rsvd; // reserved (must be zeroed) + + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvSetSleepModeParams)) | (1u << 16); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NvFrameMarkerParams + { + public uint version; + public ulong frameID; + public uint markerType; + public ulong rsvd0; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 56)] + public byte[] rsvd; + + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvFrameMarkerParams)) | (1u << 16); } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 4ccb0f170a4f..0b95adb2bb12 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -9,7 +9,6 @@ using osu.Desktop.Windows; using osu.Framework; using osu.Framework.Development; -using osu.Framework.Graphics.Rendering.LowLatency; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game; @@ -141,7 +140,6 @@ public static void Main(string[] args) else { host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); - host.SetLowLatencyMode(LatencyMode.On); host.Run(new OsuGameDesktop(args) { IsFirstRun = isFirstRun diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index a8b127d5229a..309034509769 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Configuration; using osu.Framework.Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering.LowLatency; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; @@ -26,6 +27,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi { var renderer = config.GetBindable(FrameworkSetting.Renderer); automaticRendererInUse = renderer.Value == RendererType.Automatic; + var reflexMode = config.GetBindable(FrameworkSetting.LatencyMode); Children = new Drawable[] { @@ -51,6 +53,11 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi LabelText = GraphicsSettingsStrings.ThreadingMode, Current = config.GetBindable(FrameworkSetting.ExecutionMode) }, + new SettingsEnumDropdown + { + LabelText = "Reflex", + Current = reflexMode + }, new SettingsCheckbox { LabelText = GraphicsSettingsStrings.ShowFPS, @@ -58,6 +65,11 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi }, }; + reflexMode.BindValueChanged(r => + { + host.SetLowLatencyMode(r.NewValue); + }); + renderer.BindValueChanged(r => { if (r.NewValue == host.ResolvedRenderer) From eebed88c0cfa0aa4bdf424b3174545ac2381155b Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Mon, 10 Nov 2025 09:58:58 -0500 Subject: [PATCH 03/11] Reduce Reflex code running on unsupported OS's and update reflex setting dropdown --- osu.Desktop/Program.cs | 5 +++- .../Sections/Graphics/RendererSettings.cs | 24 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0b95adb2bb12..dee1f1bbc524 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -139,7 +139,10 @@ public static void Main(string[] args) host.Run(new TournamentGame()); else { - host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); + // If we're on windows, attempt to use the NVAPI Low Latency Provider + if (OperatingSystem.IsWindows()) + host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); + host.Run(new OsuGameDesktop(args) { IsFirstRun = isFirstRun diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 309034509769..70c4c459d48d 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -22,6 +22,8 @@ public partial class RendererSettings : SettingsSubsection private bool automaticRendererInUse; + private SettingsEnumDropdown? reflexSetting; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay? dialogOverlay, OsuGame? game, GameHost host) { @@ -53,10 +55,12 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi LabelText = GraphicsSettingsStrings.ThreadingMode, Current = config.GetBindable(FrameworkSetting.ExecutionMode) }, - new SettingsEnumDropdown + reflexSetting = new SettingsEnumDropdown { - LabelText = "Reflex", - Current = reflexMode + LabelText = "NVIDIA Reflex", + Current = reflexMode, + Keywords = new[] { @"nvidia", @"latency", @"reflex" }, + TooltipText = "Reduces latency by leveraging the NVIDIA Reflex API on NVIDIA GPUs.\nRecommended to have On, turn off only if experiencing issues." }, new SettingsCheckbox { @@ -65,10 +69,20 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi }, }; + // Ensure NVIDIA reflex is turned off and hidden if the resolved renderer isn't Direct3D 11 + if (host.ResolvedRenderer != RendererType.Deferred_Direct3D11 && host.ResolvedRenderer != RendererType.Direct3D11) + { + reflexMode.Value = LatencyMode.Off; + reflexSetting.Hide(); + } + reflexMode.BindValueChanged(r => { host.SetLowLatencyMode(r.NewValue); - }); + + reflexSetting.ClearNoticeText(); + if (r.NewValue == LatencyMode.Boost) setReflexBoostNotice(); + }, true); renderer.BindValueChanged(r => { @@ -93,6 +107,8 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi }); } + private void setReflexBoostNotice() => reflexSetting?.SetNoticeText("Boost increases GPU power consumption and may increase latency in some cases. Disable Boost if experiencing issues.", true); + private partial class RendererSettingsDropdown : SettingsEnumDropdown { protected override OsuDropdown CreateDropdown() => new RendererDropdown(); From d541cabe1c222ae4778a7d65d2c1c8b7c3746d4d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Mon, 10 Nov 2025 10:29:26 -0500 Subject: [PATCH 04/11] Get rid of FPS limit for now --- osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs index 989fc2c0c2e9..e637daf56992 100644 --- a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs +++ b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs @@ -34,7 +34,7 @@ public void SetMode(LatencyMode mode) bool enable = mode != LatencyMode.Off; bool boost = mode == LatencyMode.Boost; - var status = NVAPI.SetSleepModeHelper(_deviceHandle, enable, boost, boost, 1000); + var status = NVAPI.SetSleepModeHelper(_deviceHandle, enable, boost, boost, 0); Console.WriteLine("NVAPI SetSleepMode status: " + status); } From de7af34bbef9f9a669dcf5ab906315f95777d11a Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 12:39:16 -0500 Subject: [PATCH 05/11] Only use nvapi low latency provider if NVAPI is available --- osu.Desktop/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index dee1f1bbc524..e17e6fa60581 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -139,8 +139,8 @@ public static void Main(string[] args) host.Run(new TournamentGame()); else { - // If we're on windows, attempt to use the NVAPI Low Latency Provider - if (OperatingSystem.IsWindows()) + // Attempt to use the NVAPI Low Latency Provider. This should only succeed on systems with NVIDIA GPUs and the proper drivers installed. + if (NVAPI.Available) host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); host.Run(new OsuGameDesktop(args) From ebd3829f603610452dde13c399a3e8be18aa2401 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:37:05 -0500 Subject: [PATCH 06/11] Move low latency mode switching to `FrameworkConfigManager` --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 70c4c459d48d..ff51c7c8df62 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -78,8 +78,6 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi reflexMode.BindValueChanged(r => { - host.SetLowLatencyMode(r.NewValue); - reflexSetting.ClearNoticeText(); if (r.NewValue == LatencyMode.Boost) setReflexBoostNotice(); }, true); From 6a9b7fbe9d3afd3581bf31a30090f74c26d4bfeb Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:48:28 -0500 Subject: [PATCH 07/11] Attempt to overhaul exception handling --- osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs index e637daf56992..80a8823f6a2d 100644 --- a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs +++ b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs @@ -25,6 +25,9 @@ public void Initialize(IntPtr nativeDeviceHandle) { _deviceHandle = nativeDeviceHandle; IsAvailable = NVAPI.Available && _deviceHandle != IntPtr.Zero; + + if (!IsAvailable) + throw new InvalidOperationException("NVAPI is not available or the provided device handle is invalid."); } public void SetMode(LatencyMode mode) @@ -36,7 +39,8 @@ public void SetMode(LatencyMode mode) bool boost = mode == LatencyMode.Boost; var status = NVAPI.SetSleepModeHelper(_deviceHandle, enable, boost, boost, 0); - Console.WriteLine("NVAPI SetSleepMode status: " + status); + if (status != NvStatus.OK) + throw new InvalidOperationException($"Failed to set NVAPI low latency (Sleep) mode: {status}"); } public void SetMarker(LatencyMarker marker, ulong frameId) @@ -45,6 +49,9 @@ public void SetMarker(LatencyMarker marker, ulong frameId) return; var status = NVAPI.SetLatencyMarkerHelper(_deviceHandle, (uint)marker, frameId); + + if (status != NvStatus.OK) + throw new InvalidOperationException($"Failed to set NVAPI latency marker: {status}"); } public void FrameSleep() @@ -53,6 +60,9 @@ public void FrameSleep() return; var status = NVAPI.FrameSleepHelper(_deviceHandle); + + if (status != NvStatus.OK) + throw new InvalidOperationException($"Failed to perform NVAPI frame sleep: {status}"); } } } From 78308b2f902324ebe1e235b34a6ab63c34584035 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:48:38 -0500 Subject: [PATCH 08/11] Add documentation --- .../LowLatency/NVAPILowLatencyProvider.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs index 80a8823f6a2d..2a31b6517cd5 100644 --- a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs +++ b/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs @@ -21,6 +21,11 @@ internal sealed class NVAPILowLatencyProvider : ILowLatencyProvider private IntPtr _deviceHandle; + /// + /// Initialize the NVAPI low latency provider with a native device handle. Ensure NVAPI is available before calling this method. + /// + /// An to the handle of the D3D11 device. + /// Throws an exception if NVAPI is unavailable, or the device handle provided was invalid. public void Initialize(IntPtr nativeDeviceHandle) { _deviceHandle = nativeDeviceHandle; @@ -30,6 +35,11 @@ public void Initialize(IntPtr nativeDeviceHandle) throw new InvalidOperationException("NVAPI is not available or the provided device handle is invalid."); } + /// + /// Set the low latency mode. + /// + /// The to use. + /// Throws an exception if an attempt to set the low latency mode was unsuccessful. public void SetMode(LatencyMode mode) { if (!IsAvailable || _deviceHandle == IntPtr.Zero) @@ -43,6 +53,13 @@ public void SetMode(LatencyMode mode) throw new InvalidOperationException($"Failed to set NVAPI low latency (Sleep) mode: {status}"); } + /// + /// Set a latency marker for the current frame. + /// + /// WARNING: Do not log any errors that come from this method, they should be ignored as this method runs in a realtime environment. + /// The to set. + /// The frame number this marker is for. + /// Throws an exception if the attempt to set the marker was unsuccessful. Please ensure this exception is ignored. public void SetMarker(LatencyMarker marker, ulong frameId) { if (!IsAvailable || _deviceHandle == IntPtr.Zero) @@ -54,6 +71,10 @@ public void SetMarker(LatencyMarker marker, ulong frameId) throw new InvalidOperationException($"Failed to set NVAPI latency marker: {status}"); } + /// + /// Ensure this is called once per frame, at the start of the Update phase, to allow NVAPI to manage frame sleep timing. + /// + /// Throws an exception if the Sleep attempt was unsuccessful. public void FrameSleep() { if (!IsAvailable || _deviceHandle == IntPtr.Zero) From 36b1ee38392b18d07bd3b65ed045a19dfde786d8 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:06:01 -0500 Subject: [PATCH 09/11] Disable FPS limit setting if Reflex is enabled --- .../Sections/Graphics/RendererSettings.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index ff51c7c8df62..a077704abd52 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -29,7 +29,9 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi { var renderer = config.GetBindable(FrameworkSetting.Renderer); automaticRendererInUse = renderer.Value == RendererType.Automatic; + var reflexMode = config.GetBindable(FrameworkSetting.LatencyMode); + var frameSyncMode = config.GetBindable(FrameworkSetting.FrameSync); Children = new Drawable[] { @@ -60,7 +62,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi LabelText = "NVIDIA Reflex", Current = reflexMode, Keywords = new[] { @"nvidia", @"latency", @"reflex" }, - TooltipText = "Reduces latency by leveraging the NVIDIA Reflex API on NVIDIA GPUs.\nRecommended to have On, turn off only if experiencing issues." + TooltipText = "Reduces latency by leveraging the NVIDIA Reflex API on NVIDIA GPUs.\nRecommended to have On, turn Off only if experiencing issues." }, new SettingsCheckbox { @@ -70,16 +72,24 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi }; // Ensure NVIDIA reflex is turned off and hidden if the resolved renderer isn't Direct3D 11 - if (host.ResolvedRenderer != RendererType.Deferred_Direct3D11 && host.ResolvedRenderer != RendererType.Direct3D11) + if (host.ResolvedRenderer is not (RendererType.Deferred_Direct3D11 or RendererType.Direct3D11)) { reflexMode.Value = LatencyMode.Off; reflexSetting.Hide(); } + // Disable frame limiter if reflex is enabled and add notice when reflex boost is enabled reflexMode.BindValueChanged(r => { + if (r.NewValue != LatencyMode.Off) + frameSyncMode.Disabled = true; + + frameSyncMode.Disabled = false; + reflexSetting.ClearNoticeText(); - if (r.NewValue == LatencyMode.Boost) setReflexBoostNotice(); + + if (r.NewValue == LatencyMode.Boost) + setReflexBoostNotice(); }, true); renderer.BindValueChanged(r => From 51a8eaaa53aa19ec9b3f6b3387bf36a53844873d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:09:18 -0500 Subject: [PATCH 10/11] Rename `LowLatencyProvider` to `Direct3D11LowLatencyProvider` --- ...wLatencyProvider.cs => NVAPIDirect3D11LowLatencyProvider.cs} | 2 +- osu.Desktop/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Desktop/LowLatency/{NVAPILowLatencyProvider.cs => NVAPIDirect3D11LowLatencyProvider.cs} (97%) diff --git a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs b/osu.Desktop/LowLatency/NVAPIDirect3D11LowLatencyProvider.cs similarity index 97% rename from osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs rename to osu.Desktop/LowLatency/NVAPIDirect3D11LowLatencyProvider.cs index 2a31b6517cd5..c533def7750f 100644 --- a/osu.Desktop/LowLatency/NVAPILowLatencyProvider.cs +++ b/osu.Desktop/LowLatency/NVAPIDirect3D11LowLatencyProvider.cs @@ -15,7 +15,7 @@ namespace osu.Desktop.LowLatency /// [SuppressMessage("ReSharper", "InconsistentNaming")] [SupportedOSPlatform("windows")] - internal sealed class NVAPILowLatencyProvider : ILowLatencyProvider + internal sealed class NVAPIDirect3D11LowLatencyProvider : IDirect3D11LowLatencyProvider { public bool IsAvailable { get; private set; } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index e17e6fa60581..e45c27b2d3f0 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -141,7 +141,7 @@ public static void Main(string[] args) { // Attempt to use the NVAPI Low Latency Provider. This should only succeed on systems with NVIDIA GPUs and the proper drivers installed. if (NVAPI.Available) - host.SetLowLatencyProvider(new NVAPILowLatencyProvider()); + host.SetLowLatencyProvider(new NVAPIDirect3D11LowLatencyProvider()); host.Run(new OsuGameDesktop(args) { From e65a7ab44a958408dc97745ee4311318e1e710dc Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:19:38 -0500 Subject: [PATCH 11/11] Fix small logic issue --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index a077704abd52..dd700a02e928 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -81,10 +81,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi // Disable frame limiter if reflex is enabled and add notice when reflex boost is enabled reflexMode.BindValueChanged(r => { - if (r.NewValue != LatencyMode.Off) - frameSyncMode.Disabled = true; - - frameSyncMode.Disabled = false; + frameSyncMode.Disabled = r.NewValue != LatencyMode.Off; reflexSetting.ClearNoticeText();