mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-23 22:45:05 -04:00
2da6590657
* 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>
106 lines
3.2 KiB
C++
106 lines
3.2 KiB
C++
#include "dusk/game_clock.h"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <unordered_map>
|
|
#include <dusk/frame_interpolation.h>
|
|
|
|
namespace dusk::game_clock {
|
|
|
|
using clock = std::chrono::steady_clock;
|
|
|
|
bool s_initialized = false;
|
|
clock::time_point s_previous_sample{};
|
|
clock::time_point s_current_snapshot_time{};
|
|
|
|
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
|
|
|
|
constexpr clock::duration kSimPeriodDuration =
|
|
std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(sim_pace()));
|
|
constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
|
|
constexpr int kMaxSimTicksPerFrame = 2;
|
|
|
|
void ensure_initialized() {
|
|
if (s_initialized) {
|
|
return;
|
|
}
|
|
s_previous_sample = clock::now();
|
|
s_current_snapshot_time = s_previous_sample;
|
|
s_initialized = true;
|
|
}
|
|
|
|
void reset_frame_timer() {
|
|
s_previous_sample = clock::now();
|
|
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
|
|
}
|
|
|
|
MainLoopPacer advance_main_loop() {
|
|
ensure_initialized();
|
|
|
|
const clock::time_point now = clock::now();
|
|
const clock::duration frame_gap = now - s_previous_sample;
|
|
const float presentation_dt = std::chrono::duration<float>(frame_gap).count();
|
|
s_previous_sample = now;
|
|
|
|
MainLoopPacer out{};
|
|
out.presentation_dt_seconds = presentation_dt;
|
|
|
|
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation.getValue() !=
|
|
dusk::FrameInterpMode::Off &&
|
|
!dusk::getTransientSettings().skipFrameRateLimit;
|
|
out.is_interpolating = should_interpolate;
|
|
out.sim_pace = sim_pace();
|
|
|
|
if (!should_interpolate) {
|
|
s_current_snapshot_time = now;
|
|
out.sim_ticks_to_run = 1;
|
|
return out;
|
|
}
|
|
|
|
if (frame_gap > kAbnormalGapResetThreshold) {
|
|
s_current_snapshot_time = now - kSimPeriodDuration;
|
|
out.sim_ticks_to_run = 0;
|
|
return out;
|
|
}
|
|
|
|
int sim_ticks_to_run = 0;
|
|
clock::time_point projected_snapshot_time = s_current_snapshot_time;
|
|
const clock::time_point render_time = now - kSimPeriodDuration;
|
|
while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) {
|
|
projected_snapshot_time += kSimPeriodDuration;
|
|
sim_ticks_to_run++;
|
|
}
|
|
out.sim_ticks_to_run = sim_ticks_to_run;
|
|
return out;
|
|
}
|
|
|
|
void commit_sim_tick() {
|
|
ensure_initialized();
|
|
s_current_snapshot_time += kSimPeriodDuration;
|
|
}
|
|
|
|
float sample_interpolation_step() {
|
|
ensure_initialized();
|
|
const float step =
|
|
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
|
|
return std::clamp(step, 0.0f, 1.0f);
|
|
}
|
|
|
|
float consume_interval(const void* consumer) {
|
|
ensure_initialized();
|
|
const uintptr_t key = reinterpret_cast<uintptr_t>(consumer);
|
|
const clock::time_point now = clock::now();
|
|
|
|
float dt = ui_initial_dt();
|
|
const auto it = s_interval_last_sample.find(key);
|
|
if (it != s_interval_last_sample.end()) {
|
|
dt = std::chrono::duration<float>(now - it->second).count();
|
|
dt = std::min(dt, ui_maximum_dt());
|
|
}
|
|
s_interval_last_sample[key] = now;
|
|
return dt;
|
|
}
|
|
|
|
} // namespace dusk::game_clock
|