Implement osSetTime and move update screen to before VI update

This commit is contained in:
Mr-Wiseguy 2025-08-11 01:28:41 -04:00
parent 257b5db9d6
commit d34934aa7e
4 changed files with 33 additions and 9 deletions

View file

@ -73,12 +73,21 @@ extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) {
ctx->r2 = osGetCount();
}
extern "C" void osSetCount_recomp(uint8_t * rdram, recomp_context * ctx) {
osSetCount(ctx->r4);
}
extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) {
uint64_t total_count = osGetTime();
ctx->r2 = (int32_t)(total_count >> 32);
ctx->r3 = (int32_t)(total_count >> 0);
}
extern "C" void osSetTime_recomp(uint8_t * rdram, recomp_context * ctx) {
uint64_t t = ((uint64_t)(ctx->r4) << 32) | ((ctx->r5) & 0xFFFFFFFFu);
osSetTime(t);
}
extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) {
uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu);
uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10);

View file

@ -290,7 +290,9 @@ void osViSetYScale(float scale);
PTR(void) osViGetNextFramebuffer();
PTR(void) osViGetCurrentFramebuffer();
u32 osGetCount();
void osSetCount(u32 count);
OSTime osGetTime();
void osSetTime(OSTime t);
int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg);
int osStopTimer(RDRAM_ARG PTR(OSTimer) timer);
u32 osVirtualToPhysical(PTR(void) addr);

View file

@ -28,6 +28,7 @@ struct SpTaskAction {
};
struct ScreenUpdateAction {
ultramodern::renderer::ViRegs regs;
};
struct UpdateConfigAction {
@ -55,6 +56,7 @@ static struct {
int field;
ViState states[2];
ultramodern::renderer::ViRegs regs;
ultramodern::renderer::ViRegs update_screen_regs;
ViState* get_next_state() {
return &states[cur_state ^ 1];
@ -131,7 +133,7 @@ static struct {
} events_context{};
ultramodern::renderer::ViRegs* ultramodern::renderer::get_vi_regs() {
return &events_context.vi.regs;
return &events_context.vi.update_screen_regs;
}
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
@ -198,8 +200,9 @@ void vi_thread_func() {
next = std::chrono::high_resolution_clock::now();
}
ultramodern::sleep_until(next);
auto time_now = ultramodern::time_since_start();
// Calculate how many VIs have passed
uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1;
uint64_t new_total_vis = (time_now * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1;
if (new_total_vis > total_vis + 1) {
//printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
}
@ -212,12 +215,13 @@ void vi_thread_func() {
odd = !odd;
}
// Queue a screen update for the graphics thread with the current VI register state.
// Doing this before the VI update is equivalent to updating the screen after the previous frame's scanout finished.
events_context.action_queue.enqueue(ScreenUpdateAction{ events_context.vi.regs });
// Update VI registers and swap VI modes.
events_context.vi.update_vi();
// Queue a screen update for the graphics thread.
events_context.action_queue.enqueue(ScreenUpdateAction{ });
// If the game has started, handle sending VI and AI events.
if (ultramodern::is_game_started()) {
remaining_retraces--;
@ -226,13 +230,12 @@ void vi_thread_func() {
std::lock_guard lock{ events_context.message_mutex };
ViState* cur_state = events_context.vi.get_cur_state();
if (remaining_retraces == 0) {
remaining_retraces = cur_state->retrace_count;
if (cur_state->mq != NULLPTR) {
if (osSendMesg(PASS_RDRAM cur_state->mq, cur_state->msg, OS_MESG_NOBLOCK) == -1) {
//printf("Game skipped a VI frame!\n");
}
}
remaining_retraces = cur_state->retrace_count;
}
if (events_context.ai.mq != NULLPTR) {
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
@ -370,7 +373,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
// printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
}
else if (const auto* screen_update_action = std::get_if<ScreenUpdateAction>(&action)) {
(void)screen_update_action;
events_context.vi.update_screen_regs = screen_update_action->regs;
renderer_context->update_screen();
display_refresh_rate = renderer_context->get_display_framerate();
resolution_scale = renderer_context->get_resolution_scale();

View file

@ -13,6 +13,8 @@
// Start time for the program
static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
// Offset of the duration since program start used to calculate the value for osGetTime.
static int64_t ostime_offset = 0;
// Game speed multiplier (1 means no speedup)
constexpr uint32_t speed_multiplier = 1;
// N64 CPU counter ticks per millisecond
@ -162,12 +164,20 @@ extern "C" u32 osGetCount() {
return (uint32_t)total_count;
}
extern "C" void osSetCount(u32 count) {
assert(false);
}
extern "C" OSTime osGetTime() {
uint64_t total_count = time_now();
uint64_t total_count = time_now() - ostime_offset;
return total_count;
}
extern "C" void osSetTime(OSTime t) {
ostime_offset = time_now() - t;
}
extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) {
OSTimer* t = TO_PTR(OSTimer, t_);