From bba602ab041fbc8a6e500cdb1cd2ca73cd889cfc Mon Sep 17 00:00:00 2001 From: kichikuou Date: Tue, 7 Jan 2025 13:46:44 +0900 Subject: [PATCH] Scheduler: Yield based on scenario address Now it yields execution when the device state is checked multiple times from the same scenario address. --- modules/NIGHTDLL/nt_scenario.c | 1 - src/cmdv.c | 1 - src/nact.c | 1 - src/scheduler.c | 74 +++++++++++++++++----------------- src/scheduler.h | 7 +--- src/sdl_draw.c | 3 ++ 6 files changed, 42 insertions(+), 45 deletions(-) diff --git a/modules/NIGHTDLL/nt_scenario.c b/modules/NIGHTDLL/nt_scenario.c index 6d76589..b6de289 100644 --- a/modules/NIGHTDLL/nt_scenario.c +++ b/modules/NIGHTDLL/nt_scenario.c @@ -74,7 +74,6 @@ static void ntmain(struct _scoadr inadr) { } sys_getInputInfo(); sdl_wait_vsync(); - scheduler_on_event(SCHEDULER_EVENT_NEW_FRAME); } } diff --git a/src/cmdv.c b/src/cmdv.c index ee7fb2a..19c124f 100644 --- a/src/cmdv.c +++ b/src/cmdv.c @@ -1035,7 +1035,6 @@ static void va_update() { if (!proceeding) { nact->is_va_animation = FALSE; } - scheduler_on_event(SCHEDULER_EVENT_VA_UPDATE); } void va_reset(void) { diff --git a/src/nact.c b/src/nact.c index 86d984f..8712db4 100644 --- a/src/nact.c +++ b/src/nact.c @@ -121,7 +121,6 @@ void nact_main() { nact->callback(); // Async in emscripten sys_getInputInfo(); sdl_wait_vsync(); - scheduler_on_event(SCHEDULER_EVENT_NEW_FRAME); } } } diff --git a/src/scheduler.c b/src/scheduler.c index d8a46b5..f1a3bb2 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -17,11 +17,19 @@ * */ #include "scheduler.h" +#include "scenario.h" #include "nact.h" bool scheduler_yield_requested; int scheduler_cmd_count; +#define BLOOM_FILTER_HASHES 4 + +// Knuth's multiplicative hash. +static uint32_t hash(uint32_t x, int s) { + return (x * 2654435761ULL) >> s; +} + /* * System 3.x games are basically driven by busy loops that repeatedly check * the state of input devices or timers until their state changes. On modern @@ -30,54 +38,46 @@ int scheduler_cmd_count; * loops and schedules a yield. */ void scheduler_on_event(enum scheduler_event event) { - static int frame_count = 0; - static int cmd_count_of_prev_input = -1; - static int timer_check_frame = -1; - static int timer_check_count = 0; - static int audio_check_frame = -1; - static int va_status_check_count; + static int prev_cmd_count = -1; + static uint64_t bloom = 0; switch (event) { - case SCHEDULER_EVENT_NEW_FRAME: - frame_count++; + case SCHEDULER_EVENT_SLEEP: scheduler_yield_requested = false; + bloom = 0; + scheduler_cmd_count = 0; + prev_cmd_count = -1; break; case SCHEDULER_EVENT_INPUT_CHECK_MISS: - if (scheduler_cmd_count != cmd_count_of_prev_input) - scheduler_yield_requested = true; - break; - case SCHEDULER_EVENT_INPUT_CHECK_HIT: - cmd_count_of_prev_input = scheduler_cmd_count; - break; - case SCHEDULER_EVENT_TIMER_CHECK: - // Allow up to 10 timer checks in single animation frame. - if (frame_count == timer_check_frame) { - if (++timer_check_count >= 10) - scheduler_yield_requested = true; - } else { - timer_check_frame = frame_count; - timer_check_count = 0; - } - break; - case SCHEDULER_EVENT_AUDIO_CHECK: - if (frame_count == audio_check_frame) - scheduler_yield_requested = true; - audio_check_frame = frame_count; - break; - case SCHEDULER_EVENT_VA_STATUS_CHECK: - if (++va_status_check_count > 1) { - va_status_check_count = 0; - scheduler_yield_requested = true; - } - break; + // A single Sys3x command may generate multiple scheduler events. + // Deduplicate them. + if (prev_cmd_count == scheduler_cmd_count) + break; + prev_cmd_count = scheduler_cmd_count; - case SCHEDULER_EVENT_VA_UPDATE: - va_status_check_count = 0; + if (event == SCHEDULER_EVENT_INPUT_CHECK_HIT) { + // There are new input events. Let's process them without delay. + bloom = 0; + } else { + uint32_t h1 = hash(sl_getPage(), 7); + uint32_t h2 = hash(sl_getIndex(), 15); + uint64_t bits = 0; + for (int i = 0; i < BLOOM_FILTER_HASHES; i++) { + uint32_t h = (h1 + (i + 1) * h2) % 64; + bits |= 1ULL << h; + } + if ((bloom & bits) == bits) { + request_yield(); + bloom = 0; + } else { + bloom |= bits; + } + } break; } } diff --git a/src/scheduler.h b/src/scheduler.h index 8dd1898..cedf91f 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -22,13 +22,12 @@ #include enum scheduler_event { - SCHEDULER_EVENT_NEW_FRAME, + SCHEDULER_EVENT_SLEEP, SCHEDULER_EVENT_INPUT_CHECK_MISS, SCHEDULER_EVENT_INPUT_CHECK_HIT, SCHEDULER_EVENT_TIMER_CHECK, SCHEDULER_EVENT_AUDIO_CHECK, SCHEDULER_EVENT_VA_STATUS_CHECK, - SCHEDULER_EVENT_VA_UPDATE, }; void scheduler_on_event(enum scheduler_event event); @@ -39,10 +38,8 @@ static inline bool is_yield_requested(void) { return scheduler_yield_requested; extern int scheduler_cmd_count; static inline void scheduler_on_command(void) { - if (++scheduler_cmd_count >= 10000) { + if (++scheduler_cmd_count >= 10000) request_yield(); - scheduler_cmd_count = 0; - } } #endif // __SCHEDULER__ diff --git a/src/sdl_draw.c b/src/sdl_draw.c index c22bba9..c8a97dc 100644 --- a/src/sdl_draw.c +++ b/src/sdl_draw.c @@ -36,6 +36,7 @@ #include "system.h" #include "sdl_core.h" #include "sdl_private.h" +#include "scheduler.h" #include "font.h" #include "ags.h" #include "image.h" @@ -77,6 +78,7 @@ void sdl_sleep(int msec) { #else SDL_Delay(msec); #endif + scheduler_on_event(SCHEDULER_EVENT_SLEEP); } #ifdef __EMSCRIPTEN__ @@ -98,6 +100,7 @@ void sdl_wait_vsync() { #else SDL_Delay(16); #endif + scheduler_on_event(SCHEDULER_EVENT_SLEEP); } /* off-screen の指定領域を Main Window へ転送 */