Async asset preloading + a loading phase/scene (splash) so first render doesn't stall #82

Closed
opened 2026-09-02 04:52:41 +02:00 by orkun · 1 comment
Owner

Assets are loaded synchronously inside Boot/Start (png_load, Audio.load), stalling the first frame(s) as content grows, and there is no built-in loading phase or way to show a splash/progress while assets load.

Proposal: an asset preload step (ideally async/streamed) and a first-class loading scene/phase pattern (or @Preload) with progress, so a game shows a loading screen and only enters play once assets are ready. Pairs with the namespaced asset/spritesheet API issue.

Assets are loaded synchronously inside Boot/Start (`png_load`, `Audio.load`), stalling the first frame(s) as content grows, and there is no built-in loading phase or way to show a splash/progress while assets load. Proposal: an asset preload step (ideally async/streamed) and a first-class loading scene/phase pattern (or `@Preload`) with progress, so a game shows a loading screen and only enters play once assets are ready. Pairs with the namespaced asset/spritesheet API issue.
Author
Owner

Shipped in f2cb3cd (full suite 117/0, goldens byte-identical, fixpoint intact).

Adds an incremental asset preload queue (over the #81 atlas), the deterministic no-threads form of async preloading — the work is spread across frames instead of stalling one:

  • Assets.enqueue(name, path) — queue a named image file without loading it
  • Assets.pump(max) -> int — load up to max queued assets this frame (returns how many it loaded); call it each frame in a loading scene with a small max so the frame stays short
  • Assets.total() / Assets.loaded() / Assets.ready() / Assets.progress() (0..100 percent) — drive a progress bar

The loading-scene pattern: a scene Loading Render handler pumps a few assets, draws Assets.progress() as a bar, and becomes the play scene once Assets.ready() — so the game shows a responsive loading screen and only enters play once content is ready. Loaded assets are reachable by name via Assets.get / Sprite.named. Deterministic: the same enqueue+pump order loads the same assets in the same order every run (lockstep/replay-safe).

Verified by examples/library/preload.ludic: enqueue 3, pump incrementally (progress 0 -> 33 -> 66 -> 100), ready flips, and Assets.get("hero") resolves after load — prints 3 0 0 0 1 33 66 1 100 1. 6 docs pages.

On 'ideally async/streamed': Ludic has no OS threads by design (determinism), so true background streaming isn't on the table — this per-frame incremental loader is the deterministic equivalent, and it composes with the existing Jobs cooperative scheduler if a game wants to pump from there.

Shipped in f2cb3cd (full suite 117/0, goldens byte-identical, fixpoint intact). Adds an incremental asset preload queue (over the #81 atlas), the deterministic no-threads form of async preloading — the work is spread across frames instead of stalling one: - `Assets.enqueue(name, path)` — queue a named image file without loading it - `Assets.pump(max) -> int` — load up to `max` queued assets this frame (returns how many it loaded); call it each frame in a loading scene with a small `max` so the frame stays short - `Assets.total()` / `Assets.loaded()` / `Assets.ready()` / `Assets.progress()` (0..100 percent) — drive a progress bar **The loading-scene pattern**: a `scene Loading` Render handler pumps a few assets, draws `Assets.progress()` as a bar, and `become`s the play scene once `Assets.ready()` — so the game shows a responsive loading screen and only enters play once content is ready. Loaded assets are reachable by name via `Assets.get` / `Sprite.named`. Deterministic: the same enqueue+pump order loads the same assets in the same order every run (lockstep/replay-safe). Verified by `examples/library/preload.ludic`: enqueue 3, pump incrementally (progress 0 -> 33 -> 66 -> 100), `ready` flips, and `Assets.get("hero")` resolves after load — prints `3 0 0 0 1 33 66 1 100 1`. 6 docs pages. On 'ideally async/streamed': Ludic has no OS threads by design (determinism), so true background streaming isn't on the table — this per-frame incremental loader is the deterministic equivalent, and it composes with the existing Jobs cooperative scheduler if a game wants to pump from there.
orkun closed this issue 2026-09-02 07:19:35 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: workshopsoft/ludic#82
No description provided.