mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-03 00:53:47 -04:00
Time revamp (#2304)
* Don't Use OSCalendarTime for Speedrun Timing - Shouldn't rely on OSTicksToCalendarTime since it will be changed to handle time zone conversion, and that doesn't make sense on elapsed time * Use OSGetSystemTime Extension - Fixes desyncing issues with save file time and a couple other odd instances, particularly on mobile platforms that suspend apps * Time revamp * Update aurora * Split IGT/RTA calculations * Shift-Turbo to slow down --------- Co-authored-by: SuperDude88 <82904174+SuperDude88@users.noreply.github.com>
This commit is contained in:
+86
-55
@@ -1,104 +1,135 @@
|
||||
#include "dusk/game_clock.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <aurora/time.hpp>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <dusk/frame_interpolation.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dusk::game_clock {
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using native_clock = aurora::time::native_clock;
|
||||
using game_clock = aurora::time::game_clock;
|
||||
|
||||
FrameTiming g_frameTiming;
|
||||
|
||||
namespace {
|
||||
bool s_initialized = false;
|
||||
clock::time_point s_previous_sample{};
|
||||
clock::time_point s_current_snapshot_time{};
|
||||
bool s_fixedStepActive = false;
|
||||
bool s_simTickActive = false;
|
||||
native_clock::time_point s_previousNativeSample{};
|
||||
game_clock::time_point s_latestGameSample{};
|
||||
game_clock::time_point s_currentSnapshotTime{};
|
||||
game_clock::time_point s_pendingSimTime{};
|
||||
|
||||
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
|
||||
std::unordered_map<uintptr_t, game_clock::time_point> s_intervalLastSample;
|
||||
|
||||
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;
|
||||
constexpr game_clock::duration kSimPeriodDuration =
|
||||
std::chrono::duration_cast<game_clock::duration>(std::chrono::duration<float>(kSimPeriod));
|
||||
constexpr native_clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
|
||||
constexpr int kMaxSimTicksPerFrame = static_cast<int>(aurora::time::kMaximumTimeScale) * 4;
|
||||
} // namespace
|
||||
|
||||
void ensure_initialized() {
|
||||
void initialize() {
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_previous_sample = clock::now();
|
||||
s_current_snapshot_time = s_previous_sample;
|
||||
s_previousNativeSample = native_clock::now();
|
||||
s_latestGameSample = game_clock::now();
|
||||
s_currentSnapshotTime = s_latestGameSample;
|
||||
s_pendingSimTime = s_latestGameSample;
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
void reset_frame_timer() {
|
||||
s_previous_sample = clock::now();
|
||||
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
|
||||
void reset() {
|
||||
s_previousNativeSample = native_clock::now();
|
||||
s_latestGameSample = game_clock::now();
|
||||
s_currentSnapshotTime = s_latestGameSample - kSimPeriodDuration;
|
||||
s_pendingSimTime = s_currentSnapshotTime;
|
||||
s_simTickActive = false;
|
||||
}
|
||||
|
||||
MainLoopPacer advance_main_loop() {
|
||||
ensure_initialized();
|
||||
const FrameTiming& advance() {
|
||||
const auto nativeNow = native_clock::now();
|
||||
const auto gameNow = game_clock::now();
|
||||
const auto nativeFrameGap = nativeNow - s_previousNativeSample;
|
||||
s_previousNativeSample = nativeNow;
|
||||
s_latestGameSample = gameNow;
|
||||
|
||||
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;
|
||||
auto& out = g_frameTiming;
|
||||
out = {.dt = std::chrono::duration<float>().count()};
|
||||
|
||||
MainLoopPacer out{};
|
||||
out.presentation_dt_seconds = presentation_dt;
|
||||
const float timeScale = aurora::time::scale();
|
||||
const bool interpolating =
|
||||
getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Off;
|
||||
const bool separatePresentation = interpolating || timeScale != 1.0f;
|
||||
out.interpolating = interpolating;
|
||||
out.separatePresentation = separatePresentation;
|
||||
s_fixedStepActive = separatePresentation;
|
||||
|
||||
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;
|
||||
if (!separatePresentation) {
|
||||
s_currentSnapshotTime = gameNow;
|
||||
out.numSimTicks = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (frame_gap > kAbnormalGapResetThreshold) {
|
||||
s_current_snapshot_time = now - kSimPeriodDuration;
|
||||
out.sim_ticks_to_run = 0;
|
||||
const auto simulationTarget = interpolating ? gameNow - kSimPeriodDuration : gameNow;
|
||||
if (timeScale == 0.f || nativeFrameGap > kAbnormalGapResetThreshold) {
|
||||
s_currentSnapshotTime = simulationTarget;
|
||||
out.numSimTicks = 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++;
|
||||
int numSimTicks = 0;
|
||||
auto projectedSnapshotTime = s_currentSnapshotTime;
|
||||
while (numSimTicks < kMaxSimTicksPerFrame) {
|
||||
const bool tickDue = interpolating ?
|
||||
projectedSnapshotTime < simulationTarget :
|
||||
projectedSnapshotTime + kSimPeriodDuration <= simulationTarget;
|
||||
if (!tickDue) {
|
||||
break;
|
||||
}
|
||||
projectedSnapshotTime += kSimPeriodDuration;
|
||||
numSimTicks++;
|
||||
}
|
||||
out.sim_ticks_to_run = sim_ticks_to_run;
|
||||
out.numSimTicks = numSimTicks;
|
||||
return out;
|
||||
}
|
||||
|
||||
void begin_sim_tick() {
|
||||
s_pendingSimTime =
|
||||
s_fixedStepActive ? s_currentSnapshotTime + kSimPeriodDuration : s_latestGameSample;
|
||||
s_simTickActive = true;
|
||||
}
|
||||
|
||||
void commit_sim_tick() {
|
||||
ensure_initialized();
|
||||
s_current_snapshot_time += kSimPeriodDuration;
|
||||
if (s_simTickActive) {
|
||||
s_currentSnapshotTime = s_pendingSimTime;
|
||||
s_simTickActive = false;
|
||||
} else {
|
||||
s_currentSnapshotTime += kSimPeriodDuration;
|
||||
}
|
||||
}
|
||||
|
||||
float sample_interpolation_step() {
|
||||
ensure_initialized();
|
||||
const float step =
|
||||
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
|
||||
std::chrono::duration<float>(game_clock::now() - s_currentSnapshotTime).count() /
|
||||
kSimPeriod;
|
||||
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()) {
|
||||
const auto key = reinterpret_cast<uintptr_t>(consumer);
|
||||
const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now();
|
||||
const float timeScale = aurora::time::scale();
|
||||
float dt = kUiInitialDt * timeScale;
|
||||
if (const auto it = s_intervalLastSample.find(key); it != s_intervalLastSample.end()) {
|
||||
dt = std::chrono::duration<float>(now - it->second).count();
|
||||
dt = std::min(dt, ui_maximum_dt());
|
||||
const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod);
|
||||
dt = std::min(dt, maximumDt);
|
||||
}
|
||||
s_interval_last_sample[key] = now;
|
||||
s_intervalLastSample[key] = now;
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user