diff --git a/TODO.md b/TODO.md index e52881f..dde29d2 100644 --- a/TODO.md +++ b/TODO.md @@ -1045,20 +1045,20 @@ Ordered by blast radius. Numbering continues the **Defects** list above. both functions. 31. **`akgl_game_update` segfaults if `akgl_game_init` did not run.** - `src/game.c:182` calls `game.lowfpsfunc()` through the pointer with no NULL - check, on every frame where `game.fps` is under 30 — which includes the - first frame, before there is a frame rate to compare against. `akgl_game_init` - installs the default; nothing else does. + **Fixed in 0.5.0**: `akgl_game_update_fps` installs `akgl_game_lowfps` when + it finds the hook `NULL`. - That matters because `include/akgl/renderer.h` documents the other path on - purpose: a host that owns its own window and calls `akgl_render_bind2d` - instead of `akgl_render_init2d`. An embedder following that documentation - and then calling `akgl_game_update` crashes on frame one. `akbasic` is - exactly that embedder. + Worth keeping the reasoning. `game.fps` is 0 for the first second of the + process, which is under the threshold, so the unguarded call fired on frame + one. Only `akgl_game_init` installed the default -- and + `include/akgl/renderer.h` documents the other path on purpose: a host that + owns its own window calls `akgl_render_2d_bind` instead of + `akgl_render_2d_init`. An embedder following that documentation crashed on + its first frame, and `akbasic` is exactly that embedder. - Fix: guard the call, or have `akgl_game_updateFPS` install the default when - it finds a NULL. `tests/perf_render.c` installs it by hand as a workaround - and points here. + `tests/game.c` clears the hook and calls the function, which is precisely + that state. `tests/perf_render.c` installed the default by hand as a + workaround and no longer has to. 32. **`akgl_game_update` runs the actor update sweep once per tilemap layer.** `src/game.c:617` loops `i` over `AKGL_TILEMAP_MAX_LAYERS` and the actor diff --git a/tests/perf_render.c b/tests/perf_render.c index 8b11e8e..cc2cb2a 100644 --- a/tests/perf_render.c +++ b/tests/perf_render.c @@ -793,14 +793,13 @@ int main(void) akgl_game.statelock = SDL_CreateMutex(); FAIL_ZERO_BREAK(errctx, akgl_game.statelock, AKGL_ERR_SDL, "%s", SDL_GetError()); - // And this one is not optional either. akgl_game_update_fps calls - // game.lowfpsfunc through the pointer, unguarded, on every frame under - // 30 fps -- which includes the first frame, before there is a frame rate - // to compare. A host that brings the library up the way renderer.h - // documents for an embedder, akgl_render_2d_bind over its own window, - // segfaults on its first akgl_game_update. Filed in TODO.md; installing - // the default by hand is the workaround. - akgl_game.lowfpsfunc = &akgl_game_lowfps; + // akgl_game.lowfpsfunc is deliberately left NULL. It used to have to be + // installed by hand here, because akgl_game_update_fps called it + // unguarded on every frame under 30 fps -- which includes the first, + // before there is a frame rate to compare against -- and a host that + // binds its own renderer the way renderer.h documents never went through + // akgl_game_init to get one. It installs the default itself now, and + // leaving it NULL here is what keeps that true. benchfont = TTF_OpenFont(BENCH_FONT_PATH, BENCH_FONT_SIZE); FAIL_ZERO_BREAK(errctx, benchfont, AKGL_ERR_SDL, "Couldn't open %s: %s", BENCH_FONT_PATH, SDL_GetError());