SafiApp
SafiApp owns the window, the SDL_gpu renderer, the flecs world, and the main loop. Every SafiEngine program creates exactly one.
#include <safi/core/app.h>
Types
typedef struct SafiAppDesc {
const char *title;
int width;
int height;
bool vsync;
bool enable_debug_ui;
/* Seconds per fixed-update step. 0 → default (1/60). */
float fixed_dt;
/* Max fixed steps per frame (spiral-of-death cap). 0 → default (4). */
int fixed_max_steps;
/* Root directory against which relative asset paths resolve. Scene
* files store paths in this form so they stay portable. NULL → CWD. */
const char *project_root;
/* Directory holding compiled shader artifacts. Separate from
* project_root because CMake writes shaders into the build tree.
* NULL → `<project_root>/shaders`. Exposed through
* `safi_assets_shader_root()` for loaders that take `shader_dir = NULL`. */
const char *shader_root;
/* Poll-based mtime watcher that reloads changed models/textures.
* Defaults on in debug-UI builds (editor) and off in shipping builds. */
bool enable_hot_reload;
} SafiAppDesc;
typedef struct SafiApp {
SafiRenderer renderer;
ecs_world_t *world;
bool running;
// ...internal fields...
float fixed_dt; // seconds per fixed step
float fixed_accumulator; // unconsumed dt
float fixed_elapsed; // accumulated fixed time
int fixed_max_steps; // spiral-of-death cap
} SafiApp;
Functions
safi_app_init
bool safi_app_init(SafiApp *app, const SafiAppDesc *desc);
Creates the window, initialises SDL_gpu, builds a flecs world pre-populated with the stock components, the three engine pipelines, and the engine-owned render system on EcsOnStore. Installs the SafiInput and SafiTime singletons, and optionally boots the MicroUI overlay. Returns false on failure; check SDL_GetError() for details.
safi_app_run
void safi_app_run(SafiApp *app);
Blocks the calling thread in the main loop until the window closes or a system sets app->running = false. Each frame, safi_app_tick drives three pipelines in order:
- Variable —
EcsOnLoad → … → EcsPostUpdate at wall-clock dt
- Fixed —
SafiFixedUpdate, looped N times at stable fixed_delta
- Render —
EcsPreStore → EcsOnStore at wall-clock dt
See Scheduler & Stages for the details.
safi_app_tick
bool safi_app_tick(SafiApp *app);
Runs a single frame. Useful when embedding the engine in another loop or writing tests. Returns false when the app should exit.
safi_app_shutdown
void safi_app_shutdown(SafiApp *app);
Tears everything down in reverse order (ImGui → world → renderer → SDL).
safi_app_world
static inline ecs_world_t *safi_app_world(SafiApp *app);
Returns the underlying flecs world so you can register systems and spawn entities.
Example
examples/gltf_viewer/main.c
SafiApp app;
safi_app_init(&app, &(SafiAppDesc){
.title = "SafiEngine — glTF Viewer",
.width = 1280, .height = 720,
.vsync = true,
.enable_debug_ui = true,
/* Scene files store asset paths relative to this directory. */
.project_root = SAFI_DEMO_ASSET_DIR,
/* Compiled shaders land in the CMake build dir. */
.shader_root = SAFI_DEMO_SHADER_DIR,
/* Editor auto-enables hot-reload; set explicitly in release builds
* if desired. */
});
ecs_world_t *world = safi_app_world(&app);
// ...register systems, spawn entities...
safi_app_run(&app);
safi_app_shutdown(&app);
See also