diff --git a/src/coroutine.c b/src/coroutine.c index 0f73645f..270cf09c 100644 --- a/src/coroutine.c +++ b/src/coroutine.c @@ -44,10 +44,6 @@ struct CoTask { uint32_t unique_id; }; -struct CoSched { - LIST_ANCHOR(CoTask) tasks, pending_tasks; -}; - static LIST_ANCHOR(CoTask) task_pool; static size_t num_tasks_allocated; static size_t num_tasks_in_use; @@ -283,9 +279,8 @@ void coevent_cancel(CoEvent* evt) { evt->subscribers = NULL; } -CoSched *cosched_new(void) { - CoSched *sched = calloc(1, sizeof(*sched)); - return sched; +void cosched_init(CoSched *sched) { + memset(sched, 0, sizeof(*sched)); } CoTask *cosched_new_task(CoSched *sched, CoTaskFunc func, void *arg) { @@ -319,7 +314,7 @@ uint cosched_run_tasks(CoSched *sched) { return ran; } -void cosched_free(CoSched *sched) { +void cosched_finish(CoSched *sched) { for(CoTask *t = sched->pending_tasks.first, *next; t; t = next) { next = t->next; cotask_free(t); @@ -329,6 +324,8 @@ void cosched_free(CoSched *sched) { next = t->next; cotask_free(t); } + + memset(sched, 0, sizeof(*sched)); } void coroutines_init(void) { diff --git a/src/coroutine.h b/src/coroutine.h index bd40d60f..c12f9025 100644 --- a/src/coroutine.h +++ b/src/coroutine.h @@ -43,6 +43,10 @@ typedef struct CoEvent { uint8_t num_subscribers_allocated; } CoEvent; +struct CoSched { + LIST_ANCHOR(CoTask) tasks, pending_tasks; +}; + void coroutines_init(void); void coroutines_shutdown(void); @@ -65,10 +69,10 @@ void coevent_signal(CoEvent *evt); void coevent_signal_once(CoEvent *evt); void coevent_cancel(CoEvent *evt); -CoSched *cosched_new(void); +void cosched_init(CoSched *sched); CoTask *cosched_new_task(CoSched *sched, CoTaskFunc func, void *arg); // creates and runs the task, schedules it for resume on cosched_run_tasks if it's still alive uint cosched_run_tasks(CoSched *sched); // returns number of tasks ran -void cosched_free(CoSched *sched); +void cosched_finish(CoSched *sched); static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched; } diff --git a/src/stage.c b/src/stage.c index e9c03fd4..e86176c2 100644 --- a/src/stage.c +++ b/src/stage.c @@ -677,10 +677,11 @@ bool stage_is_cleared(void) { typedef struct StageFrameState { StageInfo *stage; - int transition_delay; - uint16_t last_replay_fps; CallChain cc; + CoSched sched; + int transition_delay; int logic_calls; + uint16_t last_replay_fps; #ifdef DEBUG bool skip_to_dialog; @@ -728,6 +729,22 @@ static void stage_give_clear_bonus(const StageInfo *stage, StageClearBonus *bonu player_add_points(&global.plr, bonus->total, global.plr.pos); } +inline bool stage_should_yield(void) { + return (global.boss && !boss_is_fleeing(global.boss)) || dialog_is_active(global.dialog); +} + +void stage_yield(void) { + do { + cotask_yield(NULL); + } while(stage_should_yield()); +} + +void stage_wait(int delay) { + while(delay-- > 0) { + stage_yield(); + } +} + static LogicFrameAction stage_logic_frame(void *arg) { StageFrameState *fstate = arg; StageInfo *stage = fstate->stage; @@ -757,7 +774,9 @@ static LogicFrameAction stage_logic_frame(void *arg) { ((global.replaymode == REPLAY_PLAY) ? replay_input : stage_input)(); if(global.gameover != GAMEOVER_TRANSITIONING) { - if((!global.boss || boss_is_fleeing(global.boss)) && !dialog_is_active(global.dialog)) { + cosched_run_tasks(&fstate->sched); + + if(!stage_should_yield()) { stage->procs->event(); } @@ -839,16 +858,28 @@ static RenderFrameAction stage_render_frame(void *arg) { static void stage_end_loop(void *ctx); +static void stage_stub_proc(void) { } + void stage_enter(StageInfo *stage, CallChain next) { assert(stage); assert(stage->procs); - assert(stage->procs->preload); - assert(stage->procs->begin); - assert(stage->procs->end); - assert(stage->procs->draw); - assert(stage->procs->event); - assert(stage->procs->update); - assert(stage->procs->shader_rules); + + #define STUB_PROC(proc, stub) do {\ + if(!stage->procs->proc) { \ + stage->procs->proc = stub; \ + log_debug(#proc " proc is missing"); \ + } \ + } while(0) + + static const ShaderRule shader_rules_stub[1] = { NULL }; + + STUB_PROC(preload, stage_stub_proc); + STUB_PROC(begin, stage_stub_proc); + STUB_PROC(end, stage_stub_proc); + STUB_PROC(draw, stage_stub_proc); + STUB_PROC(event, stage_stub_proc); + STUB_PROC(update, stage_stub_proc); + STUB_PROC(shader_rules, (ShaderRule*)shader_rules_stub); if(global.gameover == GAMEOVER_WIN) { global.gameover = 0; @@ -910,14 +941,9 @@ void stage_enter(StageInfo *stage, CallChain next) { stg->playpos = 0; } - stage->procs->begin(); - player_stage_post_init(&global.plr); - - if(global.stage->type != STAGE_SPELL) { - display_stage_title(stage); - } - StageFrameState *fstate = calloc(1 , sizeof(*fstate)); + cosched_init(&fstate->sched); + cosched_set_invoke_target(&fstate->sched); fstate->stage = stage; fstate->cc = next; @@ -925,6 +951,13 @@ void stage_enter(StageInfo *stage, CallChain next) { fstate->skip_to_dialog = env_get_int("TAISEI_SKIP_TO_DIALOG", 0); #endif + stage->procs->begin(); + player_stage_post_init(&global.plr); + + if(global.stage->type != STAGE_SPELL) { + display_stage_title(stage); + } + eventloop_enter(fstate, stage_logic_frame, stage_render_frame, stage_end_loop, FPS); } @@ -944,6 +977,7 @@ void stage_end_loop(void* ctx) { stage_draw_shutdown(); stage_free(); player_free(&global.plr); + cosched_finish(&s->sched); tsrand_switch(&global.rand_visual); free_all_refs(); ent_shutdown(); diff --git a/src/stage.h b/src/stage.h index 85a9b760..5f8a3ae7 100644 --- a/src/stage.h +++ b/src/stage.h @@ -149,6 +149,10 @@ bool stage_is_cleared(void); void stage_unlock_bgm(const char *bgm); +bool stage_should_yield(void); +void stage_yield(void); +void stage_wait(int delay); + #include "stages/stage1.h" #include "stages/stage2.h" #include "stages/stage3.h" diff --git a/src/stages/corotest.c b/src/stages/corotest.c index 4c3d6e19..b63befe3 100644 --- a/src/stages/corotest.c +++ b/src/stages/corotest.c @@ -13,10 +13,6 @@ #include "global.h" #include "common_tasks.h" -static CoSched *cotest_sched; - -static void cotest_stub_proc(void) { } - TASK(laserproj_death, { Projectile *p; }) { spawn_projectile_clear_effect(ARGS.p); } @@ -144,40 +140,21 @@ TASK_FINALIZER(test_enemy) { TASK(stage_main, { int ignored; }) { YIELD; - WAIT(30); + stage_wait(30); log_debug("test 1! %i", global.timer); - WAIT(60); + stage_wait(60); log_debug("test 2! %i", global.timer); for(;;) { INVOKE_TASK_DELAYED(60, test_enemy, 9000, CMPLX(VIEWPORT_W, VIEWPORT_H) * 0.5, 3*I); - WAIT(1000); + stage_wait(1000); } } static void cotest_begin(void) { - cotest_sched = cosched_new(); - cosched_set_invoke_target(cotest_sched); INVOKE_TASK(stage_main, 0); } -static void cotest_end(void) { - cosched_free(cotest_sched); -} - -static void cotest_events(void) { - if(!global.gameover && !cosched_run_tasks(cotest_sched)) { - log_debug("over!"); - stage_finish(GAMEOVER_SCORESCREEN); - } -} - StageProcs corotest_procs = { .begin = cotest_begin, - .preload = cotest_stub_proc, - .end = cotest_end, - .draw = cotest_stub_proc, - .update = cotest_stub_proc, - .event = cotest_events, - .shader_rules = (ShaderRule[]) { NULL }, };