mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-08 12:16:17 -04:00
Frame Interp: Game Clock Refactor & Ring Item Selection
This commit is contained in:
@@ -1346,6 +1346,7 @@ set(DUSK_FILES
|
||||
src/dusk/file_select.cpp
|
||||
src/dusk/file_select.hpp
|
||||
src/dusk/frame_interpolation.cpp
|
||||
src/dusk/game_clock.cpp
|
||||
src/dusk/globals.cpp
|
||||
src/dusk/gyro.cpp
|
||||
src/dusk/io.cpp
|
||||
|
||||
@@ -204,6 +204,9 @@ private:
|
||||
/* 0x6D1 */ u8 field_0x6d1;
|
||||
/* 0x6D2 */ u8 field_0x6d2;
|
||||
/* 0x6D3 */ u8 field_0x6d3;
|
||||
#if TARGET_PC
|
||||
f32 mSelectItemSlideElapsed[4];
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* D_MENU_D_MENU_RING_H */
|
||||
|
||||
@@ -22,6 +22,7 @@ float get_interpolation_step();
|
||||
void request_presentation_sync();
|
||||
bool presentation_sync_active();
|
||||
|
||||
// TODO: These should be phased out as UI is progressively updated to use game_clock
|
||||
void set_ui_tick_pending(bool value);
|
||||
bool get_ui_tick_pending();
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef DUSK_GAME_CLOCK_H
|
||||
#define DUSK_GAME_CLOCK_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace dusk {
|
||||
namespace game_clock {
|
||||
|
||||
void ensure_initialized();
|
||||
void reset_accumulator();
|
||||
|
||||
constexpr float sim_pace() { return 1.0f / 30.0f; }
|
||||
constexpr float period_for_original_frames(float frame_count) { return frame_count * sim_pace(); }
|
||||
constexpr float ui_maximum_dt() { return 0.05f; }
|
||||
constexpr float ui_initial_dt() { return 1.0f / 60.0f; }
|
||||
|
||||
struct MainLoopPacer {
|
||||
float presentation_dt_seconds;
|
||||
bool is_interpolating;
|
||||
bool do_sim_tick;
|
||||
float interpolation_step;
|
||||
float sim_pace;
|
||||
};
|
||||
|
||||
MainLoopPacer advance_main_loop();
|
||||
|
||||
float consume_interval(const void* consumer);
|
||||
|
||||
} // namespace game_clock
|
||||
} // namespace dusk
|
||||
|
||||
#endif // DUSK_GAME_CLOCK_H
|
||||
@@ -29,6 +29,10 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/game_clock.h"
|
||||
#endif
|
||||
|
||||
typedef void (dMenu_Ring_c::*initFunc)();
|
||||
static initFunc stick_init[] = {
|
||||
/* STATUS_WAIT */ &dMenu_Ring_c::stick_wait_init,
|
||||
@@ -183,6 +187,9 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
field_0x674[i] = 0;
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[i] = 0.0f;
|
||||
#endif
|
||||
field_0x518[i] = 0.0f;
|
||||
field_0x528[i] = 0.0f;
|
||||
field_0x538[i] = 0.0f;
|
||||
@@ -1022,6 +1029,9 @@ void dMenu_Ring_c::setJumpItem(bool i_useVibrationM) {
|
||||
field_0x6b8[0] != dComIfGs_getMixItemIndex(0))
|
||||
{
|
||||
field_0x674[0] = 1;
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[0] = 0.0f;
|
||||
#endif
|
||||
}
|
||||
} else if (field_0x6b3 == 1) {
|
||||
field_0x538[0] = g_ringHIO.mUnselectItemScale;
|
||||
@@ -1030,6 +1040,9 @@ void dMenu_Ring_c::setJumpItem(bool i_useVibrationM) {
|
||||
field_0x6b8[1] != dComIfGs_getMixItemIndex(1))
|
||||
{
|
||||
field_0x674[1] = 1;
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[1] = 0.0f;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (field_0x674[0] == 1) {
|
||||
@@ -1520,7 +1533,15 @@ void dMenu_Ring_c::setSelectItem(int i_idx, u8 i_itemNo) {
|
||||
void dMenu_Ring_c::drawSelectItem() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (field_0x674[i] != 0) {
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[i] += dusk::game_clock::consume_interval(this);
|
||||
const f32 u = std::min(mSelectItemSlideElapsed[i] / dusk::game_clock::period_for_original_frames(10.0f), 1.0f);
|
||||
if (u >= 1.0f) {
|
||||
setSelectItemForce(i);
|
||||
} else {
|
||||
#else
|
||||
if (field_0x674[i] < 10) {
|
||||
#endif
|
||||
f32 initSizeX = dMeter2Info_getMeterItemPanePtr(i)->getInitSizeX() * 1.7f;
|
||||
f32 initSizeY = dMeter2Info_getMeterItemPanePtr(i)->getInitSizeY() * 1.7f;
|
||||
f32 initScaleX = dMeter2Info_getMeterItemPanePtr(i)->getInitScaleX();
|
||||
@@ -1528,7 +1549,11 @@ void dMenu_Ring_c::drawSelectItem() {
|
||||
Vec pos = dMeter2Info_getMeterItemPanePtr(i)->getGlobalVtxCenter(
|
||||
dMeter2Info_getMeterItemPanePtr(i)->mPane, true, 0);
|
||||
|
||||
#if TARGET_PC
|
||||
f32 fVar14 = 0.1f + 0.8f * u;
|
||||
#else
|
||||
f32 fVar14 = field_0x674[i] / 10.0f;
|
||||
#endif
|
||||
if (field_0x6cd != 0xff) {
|
||||
fVar14 = 1.0f - fVar14;
|
||||
}
|
||||
@@ -1549,9 +1574,11 @@ void dMenu_Ring_c::drawSelectItem() {
|
||||
0);
|
||||
}
|
||||
}
|
||||
#if !TARGET_PC
|
||||
field_0x674[i]++;
|
||||
} else {
|
||||
setSelectItemForce(i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1562,6 +1589,9 @@ void dMenu_Ring_c::setSelectItemForce(int i_idx) {
|
||||
if (field_0x674[i_idx] != 0) {
|
||||
dComIfGs_setSelectItemIndex(i_idx, field_0x6b4[i_idx]);
|
||||
field_0x674[i_idx] = 0;
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[i_idx] = 0.0f;
|
||||
#endif
|
||||
}
|
||||
} else if (field_0x674[i_idx] != 0) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
@@ -1569,6 +1599,9 @@ void dMenu_Ring_c::setSelectItemForce(int i_idx) {
|
||||
dComIfGs_setSelectItemIndex(i, field_0x6b4[i]);
|
||||
}
|
||||
field_0x674[i_idx] = 0;
|
||||
#if TARGET_PC
|
||||
mSelectItemSlideElapsed[i_idx] = 0.0f;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "dusk/game_clock.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dusk {
|
||||
namespace game_clock {
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
bool s_initialized = false;
|
||||
clock::time_point s_previous_sample{};
|
||||
float s_sim_accumulator = 0.0f;
|
||||
|
||||
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
|
||||
|
||||
void ensure_initialized() {
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_previous_sample = clock::now();
|
||||
s_sim_accumulator = sim_pace();
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
void reset_accumulator() {
|
||||
ensure_initialized();
|
||||
s_sim_accumulator = 0.0f;
|
||||
}
|
||||
|
||||
MainLoopPacer advance_main_loop() {
|
||||
ensure_initialized();
|
||||
|
||||
const clock::time_point now = clock::now();
|
||||
const float presentation_dt = std::chrono::duration<float>(now - s_previous_sample).count();
|
||||
s_previous_sample = now;
|
||||
|
||||
s_sim_accumulator += presentation_dt;
|
||||
|
||||
MainLoopPacer out{};
|
||||
out.presentation_dt_seconds = presentation_dt;
|
||||
|
||||
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit;
|
||||
out.is_interpolating = should_interpolate;
|
||||
out.sim_pace = sim_pace();
|
||||
|
||||
if (!should_interpolate) {
|
||||
s_sim_accumulator = 0.0f;
|
||||
out.do_sim_tick = true;
|
||||
out.interpolation_step = 0.0f;
|
||||
return out;
|
||||
} else {
|
||||
out.do_sim_tick = s_sim_accumulator >= sim_pace();
|
||||
out.interpolation_step = out.do_sim_tick ? 0.0f : s_sim_accumulator / sim_pace();
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
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 game_clock
|
||||
} // namespace dusk
|
||||
+10
-16
@@ -42,7 +42,6 @@
|
||||
#include "SSystem/SComponent/c_counter.h"
|
||||
#include <cstring>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
@@ -51,6 +50,7 @@
|
||||
#include "dusk/crash_reporting.h"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/game_clock.h"
|
||||
#include "dusk/gyro.h"
|
||||
#include "dusk/imgui/ImGuiEngine.hpp"
|
||||
#include "dusk/logging.h"
|
||||
@@ -205,9 +205,7 @@ void main01(void) {
|
||||
if (preLaunchUIWindowSize.width != 0)
|
||||
mDoGph_gInf_c::setWindowSize(preLaunchUIWindowSize);
|
||||
|
||||
constexpr float kSimStepSeconds = 1.0 / 30.0;
|
||||
auto previous_time = std::chrono::steady_clock::now();
|
||||
float accumulator = kSimStepSeconds;
|
||||
dusk::game_clock::ensure_initialized();
|
||||
|
||||
do {
|
||||
// 1. Update Window Events
|
||||
@@ -234,10 +232,7 @@ void main01(void) {
|
||||
|
||||
eventsDone:;
|
||||
|
||||
auto current_time = std::chrono::steady_clock::now();
|
||||
float frame_seconds = std::chrono::duration<float>(current_time - previous_time).count();
|
||||
previous_time = current_time;
|
||||
accumulator += frame_seconds;
|
||||
const dusk::game_clock::MainLoopPacer pacing = dusk::game_clock::advance_main_loop();
|
||||
|
||||
VIWaitForRetrace();
|
||||
|
||||
@@ -247,16 +242,16 @@ void main01(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) {
|
||||
if (accumulator >= kSimStepSeconds) {
|
||||
if (pacing.is_interpolating) {
|
||||
if (pacing.do_sim_tick) {
|
||||
dusk::frame_interp::set_ui_tick_pending(true);
|
||||
mDoCPd_c::read();
|
||||
dusk::gyro::read(kSimStepSeconds);
|
||||
dusk::gyro::read(pacing.sim_pace);
|
||||
fapGm_Execute();
|
||||
mDoAud_Execute();
|
||||
accumulator = 0.0f;
|
||||
dusk::game_clock::reset_accumulator();
|
||||
}
|
||||
dusk::frame_interp::interpolate(static_cast<float>(accumulator / kSimStepSeconds));
|
||||
dusk::frame_interp::interpolate(pacing.interpolation_step);
|
||||
{
|
||||
dusk::frame_interp::PresentationCameraScope presentation_camera;
|
||||
cAPIGph_Painter();
|
||||
@@ -264,11 +259,10 @@ void main01(void) {
|
||||
dusk::frame_interp::set_ui_tick_pending(false);
|
||||
} else {
|
||||
dusk::frame_interp::set_ui_tick_pending(true);
|
||||
accumulator = 0.0f;
|
||||
|
||||
|
||||
// Game Inputs
|
||||
mDoCPd_c::read();
|
||||
dusk::gyro::read(frame_seconds);
|
||||
dusk::gyro::read(pacing.presentation_dt_seconds);
|
||||
|
||||
// EXECUTE GAME LOGIC & RENDER
|
||||
// This calls mDoGph_Painter -> JFWDisplay -> GX Functions
|
||||
|
||||
Reference in New Issue
Block a user