From 3aac20dcd7ad6800848880770bdf80d262c29b27 Mon Sep 17 00:00:00 2001 From: Patrick Baran Date: Sat, 26 Sep 2026 08:26:21 +0100 Subject: [PATCH 1/2] Add Window frame rate preferences --- Engine/system/api/iosapi.h | 1 + Engine/system/api/iosapi.mm | 15 +++++++++++++++ Engine/system/systemapi.cpp | 7 +++++++ Engine/system/systemapi.h | 3 +++ Engine/ui/window.cpp | 8 ++++++++ Engine/ui/window.h | 9 +++++++++ 6 files changed, 43 insertions(+) diff --git a/Engine/system/api/iosapi.h b/Engine/system/api/iosapi.h index c9f837dc..d05e99d0 100644 --- a/Engine/system/api/iosapi.h +++ b/Engine/system/api/iosapi.h @@ -19,6 +19,7 @@ class iOSApi final: SystemApi { Rect implWindowClientRect(SystemApi::Window *w) override; bool implSetAsFullscreen(SystemApi::Window *w, bool fullScreen) override; bool implIsFullscreen(SystemApi::Window *w) override; + void implSetPreferredFrameRateRange(SystemApi::Window *w, uint32_t minimum, uint32_t maximum, uint32_t preferred) override; void implSetCursorPosition(SystemApi::Window *w, int x, int y) override; void implShowCursor(SystemApi::Window *w, CursorShape cursor) override; diff --git a/Engine/system/api/iosapi.mm b/Engine/system/api/iosapi.mm index 0f78f751..37598af4 100644 --- a/Engine/system/api/iosapi.mm +++ b/Engine/system/api/iosapi.mm @@ -6,6 +6,7 @@ #ifdef __IOS__ #import +#include #include #include #include @@ -617,4 +618,18 @@ static void appleMain(void*) { } +void iOSApi::implSetPreferredFrameRateRange(Window* w, uint32_t minimum, uint32_t maximum, uint32_t preferred) { + auto wx = reinterpret_cast(w); + if(maximum==0) { + wx->displayLink.preferredFrameRateRange = CAFrameRateRangeDefault; + return; + } + + maximum = std::min(maximum,uint32_t(wx.screen.maximumFramesPerSecond)); + minimum = std::clamp(minimum,1u,maximum); + if(preferred!=0) + preferred = std::clamp(preferred,minimum,maximum); + wx->displayLink.preferredFrameRateRange = CAFrameRateRangeMake(minimum,maximum,preferred); + } + #endif diff --git a/Engine/system/systemapi.cpp b/Engine/system/systemapi.cpp index 04e3d1dd..96d3b579 100644 --- a/Engine/system/systemapi.cpp +++ b/Engine/system/systemapi.cpp @@ -24,6 +24,13 @@ SystemApi::Data SystemApi::m; SystemApi::SystemApi() { } +void SystemApi::implSetPreferredFrameRateRange(Window*, uint32_t, uint32_t, uint32_t) { + } + +void SystemApi::setPreferredFrameRateRange(Window *w, uint32_t minimum, uint32_t maximum, uint32_t preferred) { + return inst().implSetPreferredFrameRateRange(w,minimum,maximum,preferred); + } + float SystemApi::implUiScale(Window* w) { // default to 1.0, on platforms without hidpi implementation return 1; diff --git a/Engine/system/systemapi.h b/Engine/system/systemapi.h index 31482d0f..bc175b4c 100644 --- a/Engine/system/systemapi.h +++ b/Engine/system/systemapi.h @@ -45,6 +45,8 @@ class SystemApi { static void destroyWindow(Window* w); static void exit(); + static void setPreferredFrameRateRange(SystemApi::Window *w, uint32_t minimum, uint32_t maximum, uint32_t preferred); + static Rect windowClientRect(SystemApi::Window *w); static bool setAsFullscreen(SystemApi::Window *w, bool fullScreen); @@ -80,6 +82,7 @@ class SystemApi { virtual void implShowCursor(SystemApi::Window *w, CursorShape show) = 0; virtual float implUiScale(SystemApi::Window* w); + virtual void implSetPreferredFrameRateRange(SystemApi::Window *w, uint32_t minimum, uint32_t maximum, uint32_t preferred); virtual bool implIsRunning() = 0; virtual int implExec(AppCallBack& cb) = 0; diff --git a/Engine/ui/window.cpp b/Engine/ui/window.cpp index de00eb2c..fae94f18 100644 --- a/Engine/ui/window.cpp +++ b/Engine/ui/window.cpp @@ -56,6 +56,14 @@ void Window::setCursorPosition(const Point& p) { SystemApi::setCursorPosition(hwnd(),p.x,p.y); } +void Window::setPreferredFrameRate(uint32_t fps) { + setPreferredFrameRateRange(fps,fps,fps); + } + +void Window::setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred) { + SystemApi::setPreferredFrameRateRange(hwnd(),minimum,maximum,preferred); + } + void Window::implShowCursor(CursorShape s) { resolvedCursor = s; SystemApi::showCursor(hwnd(),s); diff --git a/Engine/ui/window.h b/Engine/ui/window.h index 9217073e..7f06b8ab 100644 --- a/Engine/ui/window.h +++ b/Engine/ui/window.h @@ -21,6 +21,15 @@ class Window : public Widget { Window( ShowMode sm ); ~Window() override; + // Requests a display callback rate, not a guaranteed rendering rate. + // Zero restores the system default. Ignored on unsupported platforms. + void setPreferredFrameRate(uint32_t fps); + + // Rates are limited to the screen's maximum; minimum and preferred are + // clamped to the resulting range. Zero minimum means 1, zero preferred + // leaves the choice to the system, and zero maximum restores the default. + void setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred = 0); + void setWindowTitle(const char* utf8); protected: From 5eb895c73e080b2d17bfeb7a7ec8190839c3238b Mon Sep 17 00:00:00 2001 From: Patrick Baran Date: Sat, 26 Sep 2026 22:20:04 +0100 Subject: [PATCH 2/2] Move frame rate preferences from Window to Swapchain --- Engine/gapi/abstractgraphicsapi.h | 1 + Engine/gapi/metal/mtswapchain.h | 3 +++ Engine/gapi/metal/mtswapchain.mm | 6 ++++++ Engine/graphics/swapchain.cpp | 8 ++++++++ Engine/graphics/swapchain.h | 9 +++++++++ Engine/ui/window.cpp | 8 -------- Engine/ui/window.h | 9 --------- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Engine/gapi/abstractgraphicsapi.h b/Engine/gapi/abstractgraphicsapi.h index 7938dfe0..cf4ba7c7 100644 --- a/Engine/gapi/abstractgraphicsapi.h +++ b/Engine/gapi/abstractgraphicsapi.h @@ -522,6 +522,7 @@ namespace Tempest { struct Swapchain:NoCopy { virtual ~Swapchain()=default; virtual void reset()=0; + virtual void setPreferredFrameRateRange(uint32_t, uint32_t, uint32_t) {} virtual uint32_t currentBackBufferIndex()=0; virtual NonUniqResId syncId() const; virtual uint32_t imageCount() const=0; diff --git a/Engine/gapi/metal/mtswapchain.h b/Engine/gapi/metal/mtswapchain.h index 0f250bd1..b4fcfef3 100644 --- a/Engine/gapi/metal/mtswapchain.h +++ b/Engine/gapi/metal/mtswapchain.h @@ -22,6 +22,9 @@ class MtSwapchain : public AbstractGraphicsApi::Swapchain { ~MtSwapchain(); void reset() override; +#ifdef __IOS__ + void setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred) override; +#endif uint32_t currentBackBufferIndex() override; uint32_t imageCount() const override; uint32_t w() const override; diff --git a/Engine/gapi/metal/mtswapchain.mm b/Engine/gapi/metal/mtswapchain.mm index e0eaf26c..3d82e04b 100644 --- a/Engine/gapi/metal/mtswapchain.mm +++ b/Engine/gapi/metal/mtswapchain.mm @@ -156,6 +156,12 @@ static CGRect windowRect(UIWindow* wnd) { return currentImg; } +#ifdef __IOS__ +void MtSwapchain::setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred) { + SystemApi::setPreferredFrameRateRange(reinterpret_cast(pimpl->wnd),minimum,maximum,preferred); + } +#endif + void MtSwapchain::present() { auto pool = NsPtr::init(); diff --git a/Engine/graphics/swapchain.cpp b/Engine/graphics/swapchain.cpp index 564c7996..e0fcf8cd 100644 --- a/Engine/graphics/swapchain.cpp +++ b/Engine/graphics/swapchain.cpp @@ -44,6 +44,14 @@ void Swapchain::reset() { implReset(); } +void Swapchain::setPreferredFrameRate(uint32_t fps) { + setPreferredFrameRateRange(fps,fps,fps); + } + +void Swapchain::setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred) { + impl.handler->setPreferredFrameRateRange(minimum,maximum,preferred); + } + uint32_t Swapchain::imageCount() const { return impl.handler->imageCount(); } diff --git a/Engine/graphics/swapchain.h b/Engine/graphics/swapchain.h index f10513a8..5e74aa96 100644 --- a/Engine/graphics/swapchain.h +++ b/Engine/graphics/swapchain.h @@ -21,6 +21,15 @@ class Swapchain final { void reset(); + // Requests a display callback rate, not a guaranteed rendering rate. + // Zero restores the system default. Ignored on unsupported platforms. + void setPreferredFrameRate(uint32_t fps); + + // Rates are limited to the screen's maximum; minimum and preferred are + // clamped to the resulting range. Zero minimum means 1, zero preferred + // leaves the choice to the system, and zero maximum restores the default. + void setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred = 0); + uint32_t currentImage() const; uint32_t imageCount() const; Attachment& operator[](size_t id); diff --git a/Engine/ui/window.cpp b/Engine/ui/window.cpp index fae94f18..de00eb2c 100644 --- a/Engine/ui/window.cpp +++ b/Engine/ui/window.cpp @@ -56,14 +56,6 @@ void Window::setCursorPosition(const Point& p) { SystemApi::setCursorPosition(hwnd(),p.x,p.y); } -void Window::setPreferredFrameRate(uint32_t fps) { - setPreferredFrameRateRange(fps,fps,fps); - } - -void Window::setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred) { - SystemApi::setPreferredFrameRateRange(hwnd(),minimum,maximum,preferred); - } - void Window::implShowCursor(CursorShape s) { resolvedCursor = s; SystemApi::showCursor(hwnd(),s); diff --git a/Engine/ui/window.h b/Engine/ui/window.h index 7f06b8ab..9217073e 100644 --- a/Engine/ui/window.h +++ b/Engine/ui/window.h @@ -21,15 +21,6 @@ class Window : public Widget { Window( ShowMode sm ); ~Window() override; - // Requests a display callback rate, not a guaranteed rendering rate. - // Zero restores the system default. Ignored on unsupported platforms. - void setPreferredFrameRate(uint32_t fps); - - // Rates are limited to the screen's maximum; minimum and preferred are - // clamped to the resulting range. Zero minimum means 1, zero preferred - // leaves the choice to the system, and zero maximum restores the default. - void setPreferredFrameRateRange(uint32_t minimum, uint32_t maximum, uint32_t preferred = 0); - void setWindowTitle(const char* utf8); protected: