#include "app.h" #include "arena.h" #include "string.h" #include "scratch.h" #include "sys.h" #include "work.h" #include "user.h" #include "game.h" #include "playback.h" #include "log.h" #include "console.h" #include "resource.h" #include "asset_cache.h" #include "font.h" #include "texture.h" #include "ttf.h" #include "sheet.h" #include "mixer.h" #include "sound.h" #include "util.h" #include "settings.h" #include "draw.h" #define WRITE_DIR "power_play" GLOBAL struct { struct arena arena; struct string write_path; struct sync_flag quit_sf; } L = { 0 } DEBUG_LVAR(L_app); /* ========================== * * Write directory * ========================== */ INTERNAL struct string initialize_write_directory(struct arena *arena, struct string write_dir) { struct temp_arena scratch = scratch_begin(arena); /* Create write path */ struct string base_write_dir = sys_get_write_path(scratch.arena); struct string write_path_fmt = base_write_dir.len > 0 ? STR("%F/%F/") : STR("%F%F/"); struct string write_path = string_format( arena, write_path_fmt, FMT_STR(base_write_dir), FMT_STR(write_dir) ); /* Create write dir if not present */ if (!sys_is_dir(write_path)) { sys_mkdir(write_path); /* TODO: handle failure */ } scratch_end(scratch); return write_path; } struct string app_write_path_cat(struct arena *arena, struct string filename) { return string_cat(arena, L.write_path, filename); } /* ========================== * * Entry point * ========================== */ void app_entry_point(void) { L.quit_sf = sync_flag_alloc(); u32 worker_count = 4; { /* FIXME: Switch this on to utilize all cores. Only decreasing worker count for testing purposes. */ #if !PROFILING && !RTC /* 1. User thread, Input thread * 2. Game thread * 3. Playback thread */ u32 num_reserved_cores = 3; i32 min_worker_count = 2; i32 max_worker_count = 512; i32 target_worker_count = (i32)sys_num_logical_processors() - num_reserved_cores; worker_count = (u32)clamp_i32(target_worker_count, min_worker_count, max_worker_count); #endif } L.arena = arena_alloc(GIGABYTE(64)); L.write_path = initialize_write_directory(&L.arena, STR(WRITE_DIR)); /* Startup logging */ { struct temp_arena scratch = scratch_begin_no_conflict(); struct string logfile_path = app_write_path_cat(scratch.arena, STR("log.txt")); log_startup(logfile_path); scratch_end(scratch); } logf_info("Startup"); console_startup(); /* Startup window & renderer */ struct sys_window window = sys_window_alloc(); /* Read window settings from file */ struct sys_window_settings window_settings = settings_default_window_settings(&window); settings_read_from_file(&window_settings); sys_window_update_settings(&window, &window_settings); renderer_startup(&window); /* Startup subsystems */ resource_startup(); asset_cache_startup(); ttf_startup(); font_startup(); texture_startup(); sheet_startup(); mixer_startup(); sound_startup(); draw_startup(); /* Startup threaded systems */ user_startup(&window); work_startup(worker_count); game_startup(); playback_startup(); sys_window_show(&window); /* Wait for app_quit() */ sync_flag_wait(&L.quit_sf); /* Shutdown threaded systems */ /* FIXME: Only wait on threads for a certain period of time before * forcing process exit (to prevent process hanging in the background * when a thread gets stuck) */ playback_shutdown(); game_shutdown(); work_shutdown(); user_shutdown(); /* Write window settings to file */ struct sys_window_settings settings = sys_window_get_settings(&window); settings_write_to_file(&settings); logf_info("Program exited normally"); } void app_quit(void) { sync_flag_set(&L.quit_sf); }