Repository navigation
Description
ui_run in runtime/nvn/vulkan/device.c (line 2150 at d5344d7, added in 202916d) saves the pipeline cache and the settings when the app goes to the background:
while (SDL_WaitEventTimeout(&e, 16)) {
...
#ifdef __ANDROID__
if (e.type == SDL_EVENT_WILL_ENTER_BACKGROUND && g_vk.dev) {
vk_pipeline_cache_save();
settings_save();
}
#endif
From reading SDL, that event never comes out of the queue, so the block is dead code.
Why
SDL_SendAppEvent (src/events/SDL_events.c) does not queue the six app lifecycle events (TERMINATING, LOW_MEMORY, WILL_ENTER_BACKGROUND, DID_ENTER_BACKGROUND, WILL_ENTER_FOREGROUND, DID_ENTER_FOREGROUND). It only calls the event watchers:
// We won't actually queue this event, it needs to be handled in this call stack by an event watcher
SDL_CallEventWatchers(&event);
The function is the same in 3.4.16, which android/deps/build_deps.sh builds, and in 3.4.18. On Android the path is Android_PumpEvents -> Android_OnPause -> SDL_OnApplicationWillEnterBackground -> SDL_SendAppEvent.
The Android build installs no watcher or filter: the only SDL_AddEventWatch in the runtime is the iOS one in runtime/nvn/metal.m (background_watch), which exists for this same reason.
What is lost
When the system ends the app in the background:
- options changed with the menu still open (the menu saves on close) - the case the comment there names;
- pipelines compiled since the last save: the cache is otherwise written at the end of the shader preparation (
renderer_prepare_end) and at exit (backend_quit).
Not checked on a device
I have no Android device set up, so this is from the source only. A log line inside the block would settle it: send the app to the background and the line should be missing from lasrecomp.log.
Fix
The same two calls from an event watcher, installed where the window is made:
static bool SDLCALL background_watch(void *ud, SDL_Event *e) {
if (e->type == SDL_EVENT_WILL_ENTER_BACKGROUND && g_vk.dev) {
vk_pipeline_cache_save();
settings_save();
}
return true;
}
...
SDL_AddEventWatch(background_watch, NULL);
On Android the watcher runs on the thread that pumps events, inside SDL_WaitEventTimeout (SDL_WaitEventTimeoutNS calls Android_PumpEvents directly), so it is the same thread as the block it replaces.
Sharing it with iOS
The review of #238 suggested one watcher under PLAT_TOUCH for both ports (settings, rumble, audio, the hang watch's pause), with a hook for the backend. I have left that out of the iOS follow-ups: it changes the Android path, which I can't test. If you'd like it shared, the iOS half is background_watch in metal.m; I can do that part once the Android side is confirmed on a device.
(Written with GitHub Copilot.)
Activity
peiikai commentedon Nov 13, 2026
Thanks, confirmed from the source:
SDL_SendAppEventin SDL 3.4.16 only calls the event watchers for the lifecycle events, so the Android block inui_runnever runs. We'll move it into an event watcher as you describe and check it on a device with a log line. We'll leave sharing the watcher with iOS for after that.peiikai commentedon Nov 13, 2026
The fix is in #769: the save now runs from an event watcher, with a log line (
[app] background: saved pipeline cache and settings) so any Android log will show it working. We'll keep the iOS watcher inmetal.mas it is, so there's no need to share the two. Thanks again for the report.peiikai commentedon Nov 13, 2026
Fixed in beta_6 by #769 (a98996b). It will be in the next release.