From bfcb06e36a0c02e5bfae0c75484bda2a0733ab4e Mon Sep 17 00:00:00 2001 From: Zaafar Date: Sun, 5 Jan 2025 18:44:56 -0500 Subject: [PATCH] added code to Set/Get FPS Limit without the use of VSync also changed following defaults VSync by default is now set to false. FPSLimit by default is set to 60 FPS. See SimpleExample to see how to get/set FPS. --- ClickableTransparentOverlay/Overlay.cs | 57 ++++++++++++++++++++-- ClickableTransparentOverlay/Win32/Winmm.cs | 16 ++++++ Examples/SimpleExample/SampleOverlay.cs | 14 +++++- 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 ClickableTransparentOverlay/Win32/Winmm.cs diff --git a/ClickableTransparentOverlay/Overlay.cs b/ClickableTransparentOverlay/Overlay.cs index 25b8c4f..69b93de 100644 --- a/ClickableTransparentOverlay/Overlay.cs +++ b/ClickableTransparentOverlay/Overlay.cs @@ -52,6 +52,7 @@ public abstract class Overlay : IDisposable private Thread renderThread; private volatile CancellationTokenSource cancellationTokenSource; private volatile bool overlayIsReady; + private int fpslimit; private Dictionary loadedTexturesPtrs; @@ -150,7 +151,8 @@ public Overlay(string windowTitle, bool DPIAware, int windowWidth, int windowHei { this.initialWindowWidth = windowWidth; this.initialWindowHeight = windowHeight; - this.VSync = true; + this.VSync = false; + this.FPSLimit = 60; this._disposedValue = false; this.overlayIsReady = false; this.title = windowTitle; @@ -311,9 +313,38 @@ public unsafe bool ReplaceFont(FontHelper.FontLoadDelegate fontLoadDelegate) /// /// Enable or disable the vsync on the overlay. + /// You can either use the or . + /// VSync will be given the preference if both are set. /// public bool VSync; + /// + /// Gets or sets the FPS Limits of the overlay. + /// You can either use the or . + /// VSync will be given the preference if both are set. + /// + public int FPSLimit + { + get => this.fpslimit; + set + { + if (value == 0) + { + this.fpslimit = value; + _ = Winmm.MM_EndPeriod(1); + } + else if (value > 0) + { + this.fpslimit = value; + _ = Winmm.MM_BeginPeriod(1); + } + else + { + // ignore negative values. + } + } + } + /// /// Gets or sets the position of the overlay window. /// @@ -444,6 +475,11 @@ protected virtual void Dispose(bool disposing) if (disposing) { + if (this.FPSLimit > 0) + { + Winmm.MM_EndPeriod(1); + } + this.renderThread?.Join(); foreach(var key in this.loadedTexturesPtrs.Keys.ToArray()) { @@ -491,15 +527,17 @@ protected virtual Task PostInitialized() private void RunInfiniteLoop(CancellationToken token) { var stopwatch = Stopwatch.StartNew(); - float deltaTime = 0f; + var currentTimeSec = 0f; var clearColor = new Color4(0.0f); + var delayMs = 0f; + var sleepTimeMs = 0; while (!token.IsCancellationRequested) { - deltaTime = stopwatch.ElapsedTicks / (float)Stopwatch.Frequency; + currentTimeSec = stopwatch.ElapsedTicks / (float)Stopwatch.Frequency; stopwatch.Restart(); this.window.PumpEvents(); Utils.SetOverlayClickable(this.window.Handle, this.inputhandler.Update()); - this.renderer.Update(deltaTime, () => { Render(); }); + this.renderer.Update(currentTimeSec, () => { Render(); }); this.deviceContext.OMSetRenderTargets(renderView); this.deviceContext.ClearRenderTargetView(renderView, clearColor); this.renderer.Render(); @@ -507,6 +545,17 @@ private void RunInfiniteLoop(CancellationToken token) { this.swapChain.Present(1, PresentFlags.None); // Present with vsync } + else if (this.FPSLimit > 0) + { + this.swapChain.Present(0, PresentFlags.None); + delayMs = 1000f / this.FPSLimit; + currentTimeSec = stopwatch.ElapsedTicks / (float)Stopwatch.Frequency; + sleepTimeMs = (int)(delayMs - (currentTimeSec * 1000)); + if (sleepTimeMs > 0) + { + Thread.Sleep(sleepTimeMs); + } + } else { this.swapChain.Present(0, PresentFlags.None); // Present without vsync diff --git a/ClickableTransparentOverlay/Win32/Winmm.cs b/ClickableTransparentOverlay/Win32/Winmm.cs new file mode 100644 index 0000000..620b331 --- /dev/null +++ b/ClickableTransparentOverlay/Win32/Winmm.cs @@ -0,0 +1,16 @@ +namespace ClickableTransparentOverlay.Win32 +{ + using System; + using System.Runtime.InteropServices; + + internal static class Winmm + { + public const string LibraryName = "winmm.dll"; + + [DllImport(LibraryName, EntryPoint = "timeBeginPeriod")] + public static extern uint MM_BeginPeriod(uint uMilliseconds); + + [DllImport(LibraryName, EntryPoint = "timeEndPeriod")] + public static extern uint MM_EndPeriod(uint uMilliseconds); + } +} diff --git a/Examples/SimpleExample/SampleOverlay.cs b/Examples/SimpleExample/SampleOverlay.cs index e2c3f7d..2a14baa 100644 --- a/Examples/SimpleExample/SampleOverlay.cs +++ b/Examples/SimpleExample/SampleOverlay.cs @@ -7,20 +7,32 @@ internal class SampleOverlay : Overlay { private bool wantKeepDemoWindow = true; + private int FPSHelper; public SampleOverlay() : base(3840, 2160) { + this.FPSHelper = this.FPSLimit; } protected override Task PostInitialized() { - this.VSync = false; return Task.CompletedTask; } protected override void Render() { ImGui.ShowDemoWindow(ref wantKeepDemoWindow); + + if (ImGui.Begin("FPS Changer")) + { + if (ImGui.InputInt("Set FPS", ref FPSHelper)) + { + this.FPSLimit = this.FPSHelper; + } + + ImGui.End(); + } + if (!this.wantKeepDemoWindow) { this.Close();