DEMIENGINEDOCUMENTATION Everything is free
Assets & streaming · Game systems

Preload, budgets, and async loading.

Asset groups & streaming

Loading one file at a time works until a whole Skyforge level arrives at once. An asset group is a named set of assets and scenes loaded together under a memory and time budget, so a level fill never stalls a frame or blows the platform's memory.

What a group is

A group is an authored *.asset-group.json document listing roots (what to load) and a budget (memory and per-frame upload targets). Roots are asset:// or scene:// URIs. Loading a scene:// root pulls in the assets referenced by that scene, its HUD, and expanded prefabs.

{
  "format_version": 1,
  "id": "asset-group://skyforge_outpost",
  "roots": [
    "asset://levels/skyforge_outpost",
    "scene://game/skyforge_outpost"
  ],
  "budget": {
    "resident_mb": 192,
    "decoded_mb": 384,
    "upload_ms_per_frame": 3
  }
}
  • format_version — required on every authored document.
  • id — stable group ID.
  • roots — array of asset:// and scene:// URIs to resolve and load as one batch.
  • budget.resident_mb — cap in megabytes on memory kept resident.
  • budget.decoded_mb — cap in megabytes on decoded (pre-upload) memory.
  • budget.upload_ms_per_frame — milliseconds of GPU upload work allowed per frame.

upload_ms_per_frame bounds upload work per update; the memory budgets make oversized groups visible. Measure actual memory with Assets.memory_report() before choosing values.

Startup preload versus on-demand

The project document's assets array is startup preload only — the few things needed before the first frame (the title font, a placeholder). It does not load every project asset automatically. Everything else loads when a group asks for it, when its scene loads, or when script code calls Assets.load.

{
  "format_version": 1,
  "name": "skyforge",
  "assets": [
    "asset://fonts/skyforge_ui",
    "asset://textures/skyforge_placeholder"
  ]
}

Only these two load at startup. A weapon texture referenced by a prefab loads when that prefab's scene loads. Keeping the startup array tiny is how Skyforge reaches its first frame quickly.

Scene transitions

A scene transition prepares an implicit group from its scene:// root. The authored scene, HUD, expanded prefabs, and their asset dependencies become ready before activation at a frame boundary. A failed preparation leaves the active scene in place. An authored group is optional; scene references do not need to be repeated in a startup group.

local Scene = require("demi.scene")

Scene.prepare("scene://game/skyforge_outpost")  -- prepares the scene and its referenced assets

function Game:on_update()
    if Scene.is_prepared() then
        Scene.activate()   -- swap only when every root is ready
    end
end

Scene.prepare / Scene.is_prepared / Scene.activate follow the two-phase pattern from Scene & prefab API. The scene keeps its acquired assets until it is unloaded; shared assets stay resident while another owner still uses them.

Loading a group from script

The same demi.assets API loads an individual asset:// resource or an asset-group:// batch. Keep the returned request ID while waiting or cancelling.

local Assets = require("demi.assets")
local Game = {}

local request, err = Assets.load("asset-group://skyforge_outpost")
if not request or request == 0 then error(err) end

function Game:on_update()
    if not Assets.is_ready(request) then
        local p = Assets.progress(request)
        -- p.stage and p.fraction describe this request
    end
end

return Game

Assets.load(uri) returns a request ID and an error string. Assets.is_ready(request) returns a boolean. Assets.progress(request) returns a table with stage, fraction, byte counts, and any error. Call Assets.unload(uri) when this owner no longer needs the group. The full load API is in Assets, time & timers.

Next steps

  • Importers — getting sources into asset:// IDs first.
  • Cooking — turning the group's assets into platform output.
  • Scene & prefab API — prepare/activate across a level change.