emscripten: Add support for automounting persistent storage before SDL_main.

Now apps can have persistent files available during SDL_main()/SDL_AppInit()
and don't have to mess with Emscripten-specific code to prepare the filesystem
for use.

(cherry picked from commit dcc177faa4)
This commit is contained in:
Ryan C. Gordon
2026-03-25 09:58:41 -04:00
committed by Sam Lantinga
parent c546c5d335
commit 1fc5001f77
5 changed files with 107 additions and 5 deletions

View File

@@ -346,6 +346,64 @@ all has to live in memory at runtime.
[Emscripten's documentation on the matter](https://emscripten.org/docs/porting/files/packaging_files.html)
gives other options and details, and is worth a read.
Please also read the next section on persistent storage, for a little help
from SDL.
## Automount persistent storage
The file tree in Emscripten is provided by MEMFS by default, which stores all
files in RAM. This is often what you want, because it's fast and can be
accessed with the usual synchronous i/o functions like fopen or SDL_IOFromFile.
You can also write files to MEMFS, but when the browser tab goes away, so do
the files. But we want things like high scores, save games, etc, to still
exist if we reload the game later.
For this, Emscripten offers IDBFS, which backs files with the browser's
[IndexedDB](https://en.wikipedia.org/wiki/IndexedDB) functionality.
To use this, the app has to mount the IDBFS filesystem somewhere in the
virtual file tree, and then wait for it to sync up. This needs to be done in
Javascript code. The sync will not complete until at least one (but possibly
several) iterations of the mainloop have passed, which means you can not
access any saved files during main() or SDL_AppInit() by default.
SDL can solve this problem for you: it can be built to automatically mount the
persistent files from IDBFS to a specific place in the file tree and wait
until the sync has completed before calling main() or SDL_AppInit(), so to
your C code, it looks like the files were always available.
To use this functionality, set the CMake variable
`SDL_EMSCRIPTEN_PERSISTENT_PATH` to a path in the filetree where persistent
storage should be mounted:
```bash
mkdir build
cd build
emcmake cmake -DSDL_EMSCRIPTEN_PERSISTENT_PATH=/storage ..
```
You should also link your app with `-lidbfs.js`. If your project links to SDL
using CMake's find_package(SDL3), or uses `pkg-config sdl3 --libs`, this will
be handled for you when used with an SDL built with
`-DSDL_EMSCRIPTEN_PERSISTENT_PATH`.
Now `/storage` will be prepared when your program runs, and SDL_GetPrefPath()
will return a directory under that path. The storage is mounted with the
`autoPersist: true` option, so when you write to that tree, whether with
SDL APIs or other functions like fopen(), Emscripten will know it needs to
sync that data back to the persistent database, and will do so automatically
within the next few iterations of the mainloop.
It's best to assume the sync will take a few frames to complete, and the
data is not safe until it does.
To summarize how to automate this:
- Build with `emcmake cmake -DSDL_EMSCRIPTEN_PERSISTENT_PATH=/storage`
- Link your app with `-lidbfs.js` if not handled automatically.
- Write under `/storage`, or use SDL_GetPrefPath()
## Customizing index.html