From 2cb5ca06f114547c0d967dd34a6b3ceb39e07836 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Sun, 9 Nov 2025 22:52:22 -0500 Subject: [PATCH 01/10] Add in rudimentary reflex implementation --- .../LowLatency/ILowLatencyProvider.cs | 20 +++++ .../Rendering/LowLatency/LatencyMarker.cs | 17 ++++ .../Rendering/LowLatency/LatencyMode.cs | 12 +++ .../LowLatency/NoOpLowLatencyProvider.cs | 22 +++++ osu.Framework/Platform/GameHost.cs | 80 +++++++++++++++++++ osu.Framework/Threading/DrawThread.cs | 2 + 6 files changed, 153 insertions(+) create mode 100644 osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs create mode 100644 osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs create mode 100644 osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs create mode 100644 osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs diff --git a/osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs b/osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs new file mode 100644 index 0000000000..0adc5521d8 --- /dev/null +++ b/osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Framework.Graphics.Rendering.LowLatency +{ + public interface ILowLatencyProvider + { + bool IsAvailable { get; } + + void Initialize(IntPtr nativeDeviceHandle); + + void SetMode(LatencyMode mode); + + void SetMarker(LatencyMarker marker, ulong frameId); + + void FrameSleep(); + } +} diff --git a/osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs b/osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs new file mode 100644 index 0000000000..65b9e2a2ce --- /dev/null +++ b/osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Graphics.Rendering.LowLatency +{ + public enum LatencyMarker + { + SimulationStart, + SimulationEnd, + RenderSubmitStart, + RenderSubmitEnd, + PresentStart, + PresentEnd, + InputSample, + TriggerFlash + } +} diff --git a/osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs b/osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs new file mode 100644 index 0000000000..0cf129e20a --- /dev/null +++ b/osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Graphics.Rendering.LowLatency +{ + public enum LatencyMode + { + Off = 0, + On = 1, + Boost = 2 + } +} diff --git a/osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs b/osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs new file mode 100644 index 0000000000..0a4ff460e6 --- /dev/null +++ b/osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Framework.Graphics.Rendering.LowLatency +{ + internal sealed class NoOpLowLatencyProvider : ILowLatencyProvider + { + public static readonly NoOpLowLatencyProvider INSTANCE = new NoOpLowLatencyProvider(); + + public bool IsAvailable => false; + + public void Initialize(IntPtr deviceHandle) { } + + public void SetMode(LatencyMode mode) { } + + public void SetMarker(LatencyMarker marker, ulong frameId) { } + + public void FrameSleep() { } + } +} diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index a29c197f2d..32c8c8edd9 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -30,6 +30,7 @@ using osu.Framework.Graphics.OpenGL; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Deferred; +using osu.Framework.Graphics.Rendering.LowLatency; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Handlers; @@ -466,6 +467,7 @@ protected virtual void OnExited() protected virtual void UpdateFrame() { + FrameSleep(); if (Root == null) return; frameCount++; @@ -483,11 +485,15 @@ protected virtual void UpdateFrame() TypePerformanceMonitor.NewFrame(); + LatencyMark(LatencyMarker.SimulationStart, frameCount); + Root.UpdateSubTree(); Root.UpdateSubTreeMasking(); using (var buffer = drawRoots.GetForWrite()) buffer.Object = Root.GenerateDrawNodeSubtree(frameCount, buffer.Index, false); + + LatencyMark(LatencyMarker.SimulationEnd, frameCount); } private bool didRenderFrame; @@ -528,6 +534,8 @@ protected virtual void DrawFrame() try { + LatencyMark(LatencyMarker.RenderSubmitStart, frameCount); + using (drawMonitor.BeginCollecting(PerformanceCollectionType.DrawReset)) Renderer.BeginFrame(new Vector2(Window.ClientSize.Width, Window.ClientSize.Height)); @@ -560,6 +568,8 @@ protected virtual void DrawFrame() Renderer.FinishFrame(); + LatencyMark(LatencyMarker.RenderSubmitEnd, frameCount); + using (drawMonitor.BeginCollecting(PerformanceCollectionType.SwapBuffer)) Swap(); @@ -577,12 +587,16 @@ protected virtual void DrawFrame() /// protected virtual void Swap() { + LatencyMark(LatencyMarker.PresentStart, frameCount); + Renderer.SwapBuffers(); if (Window.GraphicsSurface.Type == GraphicsSurfaceType.OpenGL && Renderer.VerticalSync) // without waiting (i.e. glFinish), vsync is basically unplayable due to the extra latency introduced. // we will likely want to give the user control over this in the future as an advanced setting. Renderer.WaitUntilIdle(); + + LatencyMark(LatencyMarker.PresentEnd, frameCount); } /// @@ -1390,6 +1404,72 @@ private void setVSyncMode() DrawThread.Scheduler.Add(() => Renderer.VerticalSync = frameSyncMode.Value == FrameSync.VSync); } + private ILowLatencyProvider lowLatency = NoOpLowLatencyProvider.INSTANCE; + private bool lowLatencyInitialized; + + public void SetLowLatencyProvider(ILowLatencyProvider provider, IntPtr deviceHandle = 0) + { + lowLatency = provider ?? NoOpLowLatencyProvider.INSTANCE; + TryInitializeLowLatencyProvider(); + } + + internal void TryInitializeLowLatencyProvider() + { + if (lowLatencyInitialized || lowLatency is NoOpLowLatencyProvider) return; + + if (Renderer is not IVeldridRenderer veldridRenderer) return; + + try + { + IntPtr deviceHandle = veldridRenderer.Device.GetD3D11Info().Device; + + if (deviceHandle == IntPtr.Zero) return; + + lowLatency.Initialize(deviceHandle); + lowLatencyInitialized = true; + } + catch (Exception) + { + // ignored + } + } + + public void SetLowLatencyMode(LatencyMode mode) + { + try + { + lowLatency.SetMode(mode); + } + catch (Exception) + { + // ignored + } + } + + internal void LatencyMark(LatencyMarker marker, ulong frameId) + { + try + { + lowLatency.SetMarker(marker, frameId); + } + catch (Exception) + { + // ignored + } + } + + internal void FrameSleep() + { + try + { + lowLatency.FrameSleep(); + } + catch (Exception) + { + // ignored + } + } + /// /// Construct all input handlers for this host. The order here decides the priority given to handlers, with the earliest occurring having higher priority. /// diff --git a/osu.Framework/Threading/DrawThread.cs b/osu.Framework/Threading/DrawThread.cs index 08079fce55..884b59545f 100644 --- a/osu.Framework/Threading/DrawThread.cs +++ b/osu.Framework/Threading/DrawThread.cs @@ -30,6 +30,8 @@ protected sealed override void OnInitialize() { host.Renderer.BeginFrame(new Vector2(window.ClientSize.Width, window.ClientSize.Height)); host.Renderer.FinishFrame(); + + host.TryInitializeLowLatencyProvider(); } } From 36325f8facbbbb10288293b7eef1ceb960e4cc2c Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Mon, 10 Nov 2025 06:43:38 -0500 Subject: [PATCH 02/10] get things working --- osu.Framework/Configuration/FrameworkConfigManager.cs | 3 +++ osu.Framework/Platform/GameHost.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Framework/Configuration/FrameworkConfigManager.cs b/osu.Framework/Configuration/FrameworkConfigManager.cs index 8a1f261ab0..e7beb0a8b3 100644 --- a/osu.Framework/Configuration/FrameworkConfigManager.cs +++ b/osu.Framework/Configuration/FrameworkConfigManager.cs @@ -8,6 +8,7 @@ using System.Drawing; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; +using osu.Framework.Graphics.Rendering.LowLatency; using osu.Framework.Graphics.Video; using osu.Framework.Input; using osu.Framework.Platform; @@ -42,6 +43,7 @@ protected override void InitialiseDefaults() SetDefault(FrameworkSetting.FrameSync, FrameSync.Limit2x); SetDefault(FrameworkSetting.WindowMode, WindowMode.Windowed); SetDefault(FrameworkSetting.Renderer, RendererType.Automatic); + SetDefault(FrameworkSetting.LatencyMode, LatencyMode.Off); SetDefault(FrameworkSetting.ShowUnicode, false); SetDefault(FrameworkSetting.Locale, string.Empty); @@ -101,6 +103,7 @@ public enum FrameworkSetting Renderer, WindowMode, + LatencyMode, ConfineMouseMode, FrameSync, ExecutionMode, diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 32c8c8edd9..48f277f8ea 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1426,6 +1426,8 @@ internal void TryInitializeLowLatencyProvider() if (deviceHandle == IntPtr.Zero) return; lowLatency.Initialize(deviceHandle); + var currentLatencyMode = Config.GetBindable(FrameworkSetting.LatencyMode); + SetLowLatencyMode(currentLatencyMode.Value); lowLatencyInitialized = true; } catch (Exception) @@ -1438,6 +1440,7 @@ public void SetLowLatencyMode(LatencyMode mode) { try { + Config.GetBindable(FrameworkSetting.LatencyMode).Value = mode; lowLatency.SetMode(mode); } catch (Exception) From 6163e21975130c3c28651c824f0b765e9f7d056a Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 13 Nov 2025 17:46:25 -0500 Subject: [PATCH 03/10] Fix data-race issue --- osu.Framework/Platform/GameHost.cs | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 48f277f8ea..37207a4695 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -459,12 +459,18 @@ protected virtual void OnExited() Exited?.Invoke(); } - private readonly TripleBuffer drawRoots = new TripleBuffer(); + private readonly TripleBuffer drawRoots = new TripleBuffer(); internal Container Root { get; private set; } private ulong frameCount; + private class DrawBufferData + { + public DrawNode Node; + public ulong FrameCount; + } + protected virtual void UpdateFrame() { FrameSleep(); @@ -491,7 +497,11 @@ protected virtual void UpdateFrame() Root.UpdateSubTreeMasking(); using (var buffer = drawRoots.GetForWrite()) - buffer.Object = Root.GenerateDrawNodeSubtree(frameCount, buffer.Index, false); + { + buffer.Object ??= new DrawBufferData(); + buffer.Object.Node = Root.GenerateDrawNodeSubtree(frameCount, buffer.Index, false); + buffer.Object.FrameCount = frameCount; + } LatencyMark(LatencyMarker.SimulationEnd, frameCount); } @@ -513,7 +523,7 @@ protected virtual void DrawFrame() Renderer.AllowTearing = windowMode.Value == WindowMode.Fullscreen; - TripleBuffer.Buffer buffer; + TripleBuffer.Buffer buffer; using (drawMonitor.BeginCollecting(PerformanceCollectionType.Sleep)) { @@ -524,17 +534,21 @@ protected virtual void DrawFrame() Renderer.WaitUntilNextFrameReady(); didRenderFrame = false; - buffer = drawRoots.GetForRead(IsActive.Value ? TripleBuffer.DEFAULT_READ_TIMEOUT : 0); + buffer = drawRoots.GetForRead(IsActive.Value ? TripleBuffer.DEFAULT_READ_TIMEOUT : 0); } if (buffer == null) return; - Debug.Assert(buffer.Object != null); - try { - LatencyMark(LatencyMarker.RenderSubmitStart, frameCount); + if (buffer.Object is not DrawBufferData data) + return; + + DrawNode rootNode = data.Node; + ulong bufferFrameCount = data.FrameCount; + + LatencyMark(LatencyMarker.RenderSubmitStart, bufferFrameCount); using (drawMonitor.BeginCollecting(PerformanceCollectionType.DrawReset)) Renderer.BeginFrame(new Vector2(Window.ClientSize.Width, Window.ClientSize.Height)); @@ -547,7 +561,7 @@ protected virtual void DrawFrame() Renderer.PushDepthInfo(DepthInfo.Default); // Front pass - DrawNode.DrawOtherOpaqueInterior(buffer.Object, Renderer); + DrawNode.DrawOtherOpaqueInterior(rootNode, Renderer); Renderer.PopDepthInfo(); Renderer.SetBlendMask(BlendingMask.All); @@ -562,13 +576,13 @@ protected virtual void DrawFrame() } // Back pass - DrawNode.DrawOther(buffer.Object, Renderer); + DrawNode.DrawOther(rootNode, Renderer); Renderer.PopDepthInfo(); Renderer.FinishFrame(); - LatencyMark(LatencyMarker.RenderSubmitEnd, frameCount); + LatencyMark(LatencyMarker.RenderSubmitEnd, bufferFrameCount); using (drawMonitor.BeginCollecting(PerformanceCollectionType.SwapBuffer)) Swap(); From 225a4e59f159e76055bdf05e08ba9cd06d183b34 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 13 Nov 2025 23:04:26 -0500 Subject: [PATCH 04/10] Fix data-race in Swap() --- osu.Framework/Platform/GameHost.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 37207a4695..f2f52a182c 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -585,7 +585,11 @@ protected virtual void DrawFrame() LatencyMark(LatencyMarker.RenderSubmitEnd, bufferFrameCount); using (drawMonitor.BeginCollecting(PerformanceCollectionType.SwapBuffer)) + { + LatencyMark(LatencyMarker.PresentStart, bufferFrameCount); Swap(); + LatencyMark(LatencyMarker.PresentEnd, bufferFrameCount); + } Window.OnDraw(); didRenderFrame = true; @@ -601,16 +605,12 @@ protected virtual void DrawFrame() /// protected virtual void Swap() { - LatencyMark(LatencyMarker.PresentStart, frameCount); - Renderer.SwapBuffers(); if (Window.GraphicsSurface.Type == GraphicsSurfaceType.OpenGL && Renderer.VerticalSync) // without waiting (i.e. glFinish), vsync is basically unplayable due to the extra latency introduced. // we will likely want to give the user control over this in the future as an advanced setting. Renderer.WaitUntilIdle(); - - LatencyMark(LatencyMarker.PresentEnd, frameCount); } /// From f250d430f89fe4c11a44198b78f61082383ebae1 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:07:00 -0500 Subject: [PATCH 05/10] Bind latency mode to FrameworkConfigManager value --- osu.Framework/Platform/GameHost.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index f2f52a182c..bff27973d1 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1242,6 +1242,8 @@ private void bootstrapSceneGraph(Game game) private Bindable frameSyncMode; + private Bindable latencyMode; + private IBindable currentDisplayMode; private Bindable ignoredInputHandlers; @@ -1280,6 +1282,9 @@ protected virtual void SetupConfig(IDictionary default frameSyncMode = Config.GetBindable(FrameworkSetting.FrameSync); frameSyncMode.ValueChanged += _ => updateFrameSyncMode(); + latencyMode = Config.GetBindable(FrameworkSetting.LatencyMode); + latencyMode.BindValueChanged(mode => setLowLatencyMode(mode.NewValue), true); + #pragma warning disable 618 // pragma region can be removed 20210911 ignoredInputHandlers = Config.GetBindable(FrameworkSetting.IgnoredInputHandlers); @@ -1440,8 +1445,7 @@ internal void TryInitializeLowLatencyProvider() if (deviceHandle == IntPtr.Zero) return; lowLatency.Initialize(deviceHandle); - var currentLatencyMode = Config.GetBindable(FrameworkSetting.LatencyMode); - SetLowLatencyMode(currentLatencyMode.Value); + setLowLatencyMode(latencyMode.Value); lowLatencyInitialized = true; } catch (Exception) @@ -1450,7 +1454,7 @@ internal void TryInitializeLowLatencyProvider() } } - public void SetLowLatencyMode(LatencyMode mode) + private void setLowLatencyMode(LatencyMode mode) { try { From 4809124cf5e57ab98bfcded98f350a2f187a7e1f Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:49:48 -0500 Subject: [PATCH 06/10] Attempt to overhaul exception handling --- osu.Framework/Platform/GameHost.cs | 31 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index bff27973d1..936703585a 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1429,28 +1429,37 @@ private void setVSyncMode() public void SetLowLatencyProvider(ILowLatencyProvider provider, IntPtr deviceHandle = 0) { lowLatency = provider ?? NoOpLowLatencyProvider.INSTANCE; + Logger.Log("Low latency provider set to: " + lowLatency.GetType().ReadableName()); TryInitializeLowLatencyProvider(); } + /// + /// Attempts to initialize the low latency provider if it has not already been initialized. + /// + /// + /// This is called automatically after setting a new low latency provider via . + /// It should only be called manually if the provider was set before the renderer was initialized, or if the renderer has changed. + /// internal void TryInitializeLowLatencyProvider() { if (lowLatencyInitialized || lowLatency is NoOpLowLatencyProvider) return; - - if (Renderer is not IVeldridRenderer veldridRenderer) return; + if (Renderer is not IVeldridRenderer veldridRenderer || !Renderer.IsInitialised) return; try { - IntPtr deviceHandle = veldridRenderer.Device.GetD3D11Info().Device; + Logger.Log("Attempting to initialize low latency provider..."); + IntPtr deviceHandle = veldridRenderer.Device.GetD3D11Info().Device; if (deviceHandle == IntPtr.Zero) return; lowLatency.Initialize(deviceHandle); setLowLatencyMode(latencyMode.Value); lowLatencyInitialized = true; } - catch (Exception) + catch (Exception e) { - // ignored + // Intentionally not logged as an error, as failure is expected (this method can be run before renderer initialization). + Logger.Log("Failed to initialize low latency provider: " + e, level: LogLevel.Important); } } @@ -1458,12 +1467,11 @@ private void setLowLatencyMode(LatencyMode mode) { try { - Config.GetBindable(FrameworkSetting.LatencyMode).Value = mode; lowLatency.SetMode(mode); } - catch (Exception) + catch (Exception e) { - // ignored + logException(e, "unobserved"); } } @@ -1475,7 +1483,8 @@ internal void LatencyMark(LatencyMarker marker, ulong frameId) } catch (Exception) { - // ignored + // WARNING: Do not log anything here or otherwise catch the error. + // This method is called extremely frequently (multiple times per frame) and doing so could cause massive performance degradation. } } @@ -1485,9 +1494,9 @@ internal void FrameSleep() { lowLatency.FrameSleep(); } - catch (Exception) + catch (Exception e) { - // ignored + logException(e, "unobserved"); } } From f0ea66facf42bfd4f4b14619f3a6e71a67dab65f Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 14:49:56 -0500 Subject: [PATCH 07/10] Add a bit of documentation --- osu.Framework/Platform/GameHost.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 936703585a..247dd517a6 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1426,7 +1426,11 @@ private void setVSyncMode() private ILowLatencyProvider lowLatency = NoOpLowLatencyProvider.INSTANCE; private bool lowLatencyInitialized; - public void SetLowLatencyProvider(ILowLatencyProvider provider, IntPtr deviceHandle = 0) + /// + /// Set the low latency provider to be used by this host. + /// + /// The to use. + public void SetLowLatencyProvider(ILowLatencyProvider provider) { lowLatency = provider ?? NoOpLowLatencyProvider.INSTANCE; Logger.Log("Low latency provider set to: " + lowLatency.GetType().ReadableName()); From 5d1acafc34350c9ba21088c0960f503d991ab52d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:06:39 -0500 Subject: [PATCH 08/10] Set Draw thread FPS limit to refresh rate if Reflex is enabled --- osu.Framework/Platform/GameHost.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 247dd517a6..30079b528f 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1406,6 +1406,10 @@ private void updateFrameSyncMode() break; } + // If low latency is enabled, we want to limit the draw thread to refresh rate as anything above is unnecessary. + if (lowLatencyInitialized && latencyMode.Value != LatencyMode.Off) + drawLimiter = refreshRate; + if (!AllowBenchmarkUnlimitedFrames) { drawLimiter = Math.Min(maximum_sane_fps, drawLimiter); From 5c02d436cb408143ad08e047f9f9527fcd34b32e Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:09:42 -0500 Subject: [PATCH 09/10] Rename `LowLatencyProvider` to `Direct3D11LowLatencyProvider` --- ...er.cs => IDirect3D11LowLatencyProvider.cs} | 2 +- ...cs => NoOpDirect3D11LowLatencyProvider.cs} | 4 ++-- osu.Framework/Platform/GameHost.cs | 22 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) rename osu.Framework/Graphics/Rendering/LowLatency/{ILowLatencyProvider.cs => IDirect3D11LowLatencyProvider.cs} (89%) rename osu.Framework/Graphics/Rendering/LowLatency/{NoOpLowLatencyProvider.cs => NoOpDirect3D11LowLatencyProvider.cs} (70%) diff --git a/osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs b/osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs similarity index 89% rename from osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs rename to osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs index 0adc5521d8..6c5bc739c9 100644 --- a/osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs +++ b/osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs @@ -5,7 +5,7 @@ namespace osu.Framework.Graphics.Rendering.LowLatency { - public interface ILowLatencyProvider + public interface IDirect3D11LowLatencyProvider { bool IsAvailable { get; } diff --git a/osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs b/osu.Framework/Graphics/Rendering/LowLatency/NoOpDirect3D11LowLatencyProvider.cs similarity index 70% rename from osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs rename to osu.Framework/Graphics/Rendering/LowLatency/NoOpDirect3D11LowLatencyProvider.cs index 0a4ff460e6..f5274751ee 100644 --- a/osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs +++ b/osu.Framework/Graphics/Rendering/LowLatency/NoOpDirect3D11LowLatencyProvider.cs @@ -5,9 +5,9 @@ namespace osu.Framework.Graphics.Rendering.LowLatency { - internal sealed class NoOpLowLatencyProvider : ILowLatencyProvider + internal sealed class NoOpDirect3D11LowLatencyProvider : IDirect3D11LowLatencyProvider { - public static readonly NoOpLowLatencyProvider INSTANCE = new NoOpLowLatencyProvider(); + public static readonly NoOpDirect3D11LowLatencyProvider INSTANCE = new NoOpDirect3D11LowLatencyProvider(); public bool IsAvailable => false; diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 30079b528f..1ea2d34e4e 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1427,17 +1427,17 @@ private void setVSyncMode() DrawThread.Scheduler.Add(() => Renderer.VerticalSync = frameSyncMode.Value == FrameSync.VSync); } - private ILowLatencyProvider lowLatency = NoOpLowLatencyProvider.INSTANCE; + private IDirect3D11LowLatencyProvider direct3D11LowLatency = NoOpDirect3D11LowLatencyProvider.INSTANCE; private bool lowLatencyInitialized; /// /// Set the low latency provider to be used by this host. /// - /// The to use. - public void SetLowLatencyProvider(ILowLatencyProvider provider) + /// The to use. + public void SetLowLatencyProvider(IDirect3D11LowLatencyProvider provider) { - lowLatency = provider ?? NoOpLowLatencyProvider.INSTANCE; - Logger.Log("Low latency provider set to: " + lowLatency.GetType().ReadableName()); + direct3D11LowLatency = provider ?? NoOpDirect3D11LowLatencyProvider.INSTANCE; + Logger.Log("Low latency provider set to: " + direct3D11LowLatency.GetType().ReadableName()); TryInitializeLowLatencyProvider(); } @@ -1445,12 +1445,12 @@ public void SetLowLatencyProvider(ILowLatencyProvider provider) /// Attempts to initialize the low latency provider if it has not already been initialized. /// /// - /// This is called automatically after setting a new low latency provider via . + /// This is called automatically after setting a new low latency provider via . /// It should only be called manually if the provider was set before the renderer was initialized, or if the renderer has changed. /// internal void TryInitializeLowLatencyProvider() { - if (lowLatencyInitialized || lowLatency is NoOpLowLatencyProvider) return; + if (lowLatencyInitialized || direct3D11LowLatency is NoOpDirect3D11LowLatencyProvider) return; if (Renderer is not IVeldridRenderer veldridRenderer || !Renderer.IsInitialised) return; try @@ -1460,7 +1460,7 @@ internal void TryInitializeLowLatencyProvider() IntPtr deviceHandle = veldridRenderer.Device.GetD3D11Info().Device; if (deviceHandle == IntPtr.Zero) return; - lowLatency.Initialize(deviceHandle); + direct3D11LowLatency.Initialize(deviceHandle); setLowLatencyMode(latencyMode.Value); lowLatencyInitialized = true; } @@ -1475,7 +1475,7 @@ private void setLowLatencyMode(LatencyMode mode) { try { - lowLatency.SetMode(mode); + direct3D11LowLatency.SetMode(mode); } catch (Exception e) { @@ -1487,7 +1487,7 @@ internal void LatencyMark(LatencyMarker marker, ulong frameId) { try { - lowLatency.SetMarker(marker, frameId); + direct3D11LowLatency.SetMarker(marker, frameId); } catch (Exception) { @@ -1500,7 +1500,7 @@ internal void FrameSleep() { try { - lowLatency.FrameSleep(); + direct3D11LowLatency.FrameSleep(); } catch (Exception e) { From 489272e770dbf88630e9b31dd606e3876bfd9faf Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 20 Nov 2025 15:16:28 -0500 Subject: [PATCH 10/10] Ensure Update thread runs at 1000hz --- osu.Framework/Platform/GameHost.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs index 1ea2d34e4e..b6c2921859 100644 --- a/osu.Framework/Platform/GameHost.cs +++ b/osu.Framework/Platform/GameHost.cs @@ -1407,8 +1407,12 @@ private void updateFrameSyncMode() } // If low latency is enabled, we want to limit the draw thread to refresh rate as anything above is unnecessary. + // Keep Update thread at 1000hz for input & audio responsiveness. if (lowLatencyInitialized && latencyMode.Value != LatencyMode.Off) + { drawLimiter = refreshRate; + updateLimiter = int.MaxValue; + } if (!AllowBenchmarkUnlimitedFrames) {