mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-23 14:41:33 -04:00
feat: FPS Limiter (#1446)
* Add interpolation frame rate cap * wip: reworked framelimiter Based on my testing this is a bit more stable in frametimes. * wip: efficiency improvement + windows build fix Significantly improve efficiency by using a hybrid approach. * wip: UI changes * wip: end frame AFTER limiting * wip: remove unused include * wip: minor ui code change Makes it easier to remove/add presets * Simplify Limiter UI - Change enableFrameInterpolation to an enum with off/capped/unlimited values - Simplify the UI to use 2 settings (unlock framerate + a max value entry) * wip: slight limiter simplification * wip: implement review suggestions * wip: fix syntax error * wip: revert enum order + replace old checks * Fix compile error --------- Co-authored-by: SailorSnoW <sailorsnow@pm.me> Co-authored-by: Loïs <49660929+SailorSnoW@users.noreply.github.com> Co-authored-by: SuperDude88 <82904174+SuperDude88@users.noreply.github.com> Co-authored-by: Luke Street <luke@street.dev>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "settings.h"
|
||||
|
||||
class camera_process_class;
|
||||
class view_class;
|
||||
@@ -18,7 +19,7 @@ void begin_record();
|
||||
void end_record();
|
||||
void begin_sim_tick();
|
||||
uint64_t sim_tick_seq();
|
||||
void begin_frame(bool enabled, bool is_sim_frame, float step);
|
||||
void begin_frame(FrameInterpMode mode, bool is_sim_frame, float step);
|
||||
void interpolate();
|
||||
float get_interpolation_step();
|
||||
|
||||
|
||||
+15
-2
@@ -34,6 +34,12 @@ enum class GyroMode : u8 {
|
||||
Mouse = 1,
|
||||
};
|
||||
|
||||
enum class FrameInterpMode : u8 {
|
||||
Off = 0,
|
||||
Capped = 1,
|
||||
Unlimited = 2,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
template <>
|
||||
struct ConfigEnumRange<BloomMode> {
|
||||
@@ -58,7 +64,13 @@ struct ConfigEnumRange<GyroMode> {
|
||||
static constexpr auto min = GyroMode::Sensor;
|
||||
static constexpr auto max = GyroMode::Mouse;
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct ConfigEnumRange<FrameInterpMode> {
|
||||
static constexpr auto min = FrameInterpMode::Off;
|
||||
static constexpr auto max = FrameInterpMode::Unlimited;
|
||||
};
|
||||
} // namespace config
|
||||
|
||||
// Persistent user settings
|
||||
|
||||
@@ -72,6 +84,7 @@ struct UserSettings {
|
||||
ConfigVar<bool> lockAspectRatio;
|
||||
ConfigVar<bool> enableFpsOverlay;
|
||||
ConfigVar<int> fpsOverlayCorner;
|
||||
ConfigVar<int> maxFrameRate;
|
||||
} video;
|
||||
|
||||
struct {
|
||||
@@ -124,7 +137,7 @@ struct UserSettings {
|
||||
ConfigVar<float> bloomMultiplier;
|
||||
ConfigVar<bool> disableWaterRefraction;
|
||||
ConfigVar<bool> enableTextureReplacements;
|
||||
ConfigVar<bool> enableFrameInterpolation;
|
||||
ConfigVar<FrameInterpMode> enableFrameInterpolation;
|
||||
ConfigVar<int> internalResolutionScale;
|
||||
ConfigVar<int> shadowResolutionMultiplier;
|
||||
ConfigVar<bool> enableDepthOfField;
|
||||
|
||||
+37
-4
@@ -17,16 +17,21 @@
|
||||
#include <shellapi.h>
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
class Limiter {
|
||||
public:
|
||||
using duration_t = Uint64;
|
||||
|
||||
void Reset() { m_oldTime = SDL_GetTicksNS(); }
|
||||
void Reset() {
|
||||
m_oldTime = SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
void Sleep(duration_t targetFrameTime) {
|
||||
duration_t Sleep(duration_t targetFrameTime) {
|
||||
if (targetFrameTime == 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Uint64 start = SDL_GetTicksNS();
|
||||
@@ -41,6 +46,8 @@ public:
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
|
||||
return adjustedSleepTime;
|
||||
}
|
||||
|
||||
duration_t SleepTime(duration_t targetFrameTime) {
|
||||
@@ -74,7 +81,6 @@ private:
|
||||
if (!initialized || numSleeps++ % 1000 == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
if (QueryPerformanceFrequency(&freq) == 0) {
|
||||
DuskLog.warn("QueryPerformanceFrequency failed: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
countPerNs = static_cast<double>(freq.QuadPart) / 1e9;
|
||||
@@ -98,6 +104,33 @@ private:
|
||||
#endif
|
||||
} while (current.QuadPart - start.QuadPart < ticksToWait);
|
||||
}
|
||||
#elif defined (__APPLE__)
|
||||
void NanoSleep(const duration_t duration) {
|
||||
// Hybrid approach using Apple Mach
|
||||
uint64_t start_mach = mach_absolute_time();
|
||||
|
||||
mach_timebase_info_data_t timebase_info;
|
||||
mach_timebase_info(&timebase_info);
|
||||
|
||||
uint64_t total_mach_ticks = (duration * timebase_info.denom) / timebase_info.numer;
|
||||
uint64_t target_mach = start_mach + total_mach_ticks;
|
||||
|
||||
uint64_t buffer_ns = 2'000'000ULL;
|
||||
uint64_t buffer_mach_ticks = (buffer_ns * timebase_info.denom) / timebase_info.numer;
|
||||
|
||||
if (total_mach_ticks > buffer_mach_ticks) {
|
||||
uint64_t sleep_until_mach = target_mach - buffer_mach_ticks;
|
||||
mach_wait_until(sleep_until_mach);
|
||||
}
|
||||
|
||||
while (mach_absolute_time() < target_mach) {
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
asm volatile("yield" ::: "memory"); // Hardware hint, not a scheduler hint.
|
||||
#else
|
||||
_mm_pause();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration); }
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user