mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-27 23:31:36 -04:00
0f5a248401
* 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>
137 lines
4.4 KiB
C++
137 lines
4.4 KiB
C++
#include "dusk/game_clock.h"
|
|
|
|
#include <algorithm>
|
|
#include <aurora/time.hpp>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <dusk/frame_interpolation.h>
|
|
#include <unordered_map>
|
|
|
|
namespace dusk::game_clock {
|
|
|
|
using native_clock = aurora::time::native_clock;
|
|
using game_clock = aurora::time::game_clock;
|
|
|
|
FrameTiming g_frameTiming;
|
|
|
|
namespace {
|
|
bool s_initialized = false;
|
|
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, game_clock::time_point> s_intervalLastSample;
|
|
|
|
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 initialize() {
|
|
if (s_initialized) {
|
|
return;
|
|
}
|
|
s_previousNativeSample = native_clock::now();
|
|
s_latestGameSample = game_clock::now();
|
|
s_currentSnapshotTime = s_latestGameSample;
|
|
s_pendingSimTime = s_latestGameSample;
|
|
s_initialized = true;
|
|
}
|
|
|
|
void reset() {
|
|
s_previousNativeSample = native_clock::now();
|
|
s_latestGameSample = game_clock::now();
|
|
s_currentSnapshotTime = s_latestGameSample - kSimPeriodDuration;
|
|
s_pendingSimTime = s_currentSnapshotTime;
|
|
s_simTickActive = false;
|
|
}
|
|
|
|
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;
|
|
|
|
auto& out = g_frameTiming;
|
|
out = {.dt = std::chrono::duration<float>().count()};
|
|
|
|
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;
|
|
|
|
if (!separatePresentation) {
|
|
s_currentSnapshotTime = gameNow;
|
|
out.numSimTicks = 1;
|
|
return out;
|
|
}
|
|
|
|
const auto simulationTarget = interpolating ? gameNow - kSimPeriodDuration : gameNow;
|
|
if (timeScale == 0.f || nativeFrameGap > kAbnormalGapResetThreshold) {
|
|
s_currentSnapshotTime = simulationTarget;
|
|
out.numSimTicks = 0;
|
|
return out;
|
|
}
|
|
|
|
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.numSimTicks = numSimTicks;
|
|
return out;
|
|
}
|
|
|
|
void begin_sim_tick() {
|
|
s_pendingSimTime =
|
|
s_fixedStepActive ? s_currentSnapshotTime + kSimPeriodDuration : s_latestGameSample;
|
|
s_simTickActive = true;
|
|
}
|
|
|
|
void commit_sim_tick() {
|
|
if (s_simTickActive) {
|
|
s_currentSnapshotTime = s_pendingSimTime;
|
|
s_simTickActive = false;
|
|
} else {
|
|
s_currentSnapshotTime += kSimPeriodDuration;
|
|
}
|
|
}
|
|
|
|
float sample_interpolation_step() {
|
|
const float step =
|
|
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) {
|
|
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();
|
|
const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod);
|
|
dt = std::min(dt, maximumDt);
|
|
}
|
|
s_intervalLastSample[key] = now;
|
|
return dt;
|
|
}
|
|
|
|
} // namespace dusk::game_clock
|