#include "system.h" #include #include #include #include "../libraries/stref.h" #include "../libraries/array.h" #include "../libraries/sokol_time.h" #include "../stereokit.h" #include "../sk_memory.h" namespace sk { /////////////////////////////////////////// struct sort_dependency_t { int32_t *ids; int32_t count; }; array_t systems = {}; int32_t *system_init_order = nullptr; bool systems_initialized = false; /////////////////////////////////////////// int32_t systems_find_id(const char *name); bool systems_sort (); int32_t topological_sort (sort_dependency_t *dependencies, int32_t count, int32_t *ref_order); int32_t topological_sort_visit(sort_dependency_t *dependencies, int32_t count, int32_t index, uint8_t *marks, int32_t *sorted_curr, int32_t *out_order); /////////////////////////////////////////// void systems_add(const system_t *system) { systems.add(*system); } /////////////////////////////////////////// int32_t systems_find_id(const char *name) { for (int32_t i = 0; i < systems.count; i++) { if (string_eq(name, systems[i].name)) return (int32_t)i; } return -1; } /////////////////////////////////////////// int32_t systems_find_idx(const char* name) { for (int32_t i = 0; i < systems.count; i++) { if (string_eq(name, systems[i].name)) return i; } return -1; } /////////////////////////////////////////// system_t *systems_find(const char *name) { int32_t idx = systems_find_idx(name); return idx == -1 ? nullptr : &systems[idx]; } /////////////////////////////////////////// bool systems_sort() { int32_t result = 0; // Turn dependency names into indices for update sort_dependency_t *update_ids = sk_malloc_t(sort_dependency_t, systems.count); for (int32_t i = 0; i < systems.count; i++) { update_ids[i].count = systems[i].step_dependency_count; update_ids[i].ids = sk_malloc_t(int32_t, systems[i].step_dependency_count); for (int32_t d = 0; d < systems[i].step_dependency_count; d++) { update_ids[i].ids[d] = systems_find_id(systems[i].step_dependencies[d]); if (update_ids[i].ids[d] == -1) { log_errf("Can't find system update dependency by the name of %s!", systems[i].step_dependencies[d]); result = 1; } } } // Sort sort the update order if (result == 0) { int32_t *update_order = sk_malloc_t(int32_t, systems.count); result = topological_sort(update_ids, systems.count, update_order); if (result != 0) log_errf("Invalid update dependencies! Cyclic dependency detected at %s!", systems[result].name); else systems.reorder(update_order); sk_free(update_order); } // Turn dependency names into indices for init sort_dependency_t *init_ids = sk_malloc_t(sort_dependency_t, systems.count); for (int32_t i = 0; i < systems.count; i++) { init_ids[i].count = systems[i].init_dependency_count; init_ids[i].ids = sk_malloc_t(int32_t, systems[i].init_dependency_count); for (int32_t d = 0; d < systems[i].init_dependency_count; d++) { init_ids[i].ids[d] = systems_find_id(systems[i].init_dependencies[d]); if (init_ids[i].ids[d] == -1) { log_errf("Can't find system init dependency by the name of %s!", systems[i].init_dependencies[d]); result = 1; } } } // Sort the init order if (result == 0) { system_init_order = sk_malloc_t(int32_t, systems.count); result = topological_sort(init_ids, systems.count, system_init_order); if (result != 0) log_errf("Invalid initialization dependencies! Cyclic dependency detected at %s!", systems[result].name); } // Release memory for (int32_t i = 0; i < systems.count; i++) { sk_free(init_ids [i].ids); sk_free(update_ids[i].ids); } sk_free(init_ids); sk_free(update_ids); return result == 0; } /////////////////////////////////////////// bool systems_initialize() { if (!systems_sort()) return false; for (int32_t i = 0; i < systems.count; i++) { int32_t index = system_init_order[i]; if (systems[index].func_initialize != nullptr) { log_diagf("Initializing %s", systems[index].name); // start timing uint64_t start = stm_now(); if (!systems[index].func_initialize()) { log_errf("System %s failed to initialize!", systems[index].name); return false; } // end timing systems[index].profile_start_duration = stm_since(start); } } systems_initialized = true; log_info("Initialization successful"); return true; } /////////////////////////////////////////// void system_execute(system_t *sys) { if (sys == nullptr || sys->func_step == nullptr) return; // start timing sys->profile_frame_start = stm_now(); sys->func_step(); // end timing if (sys->profile_frame_duration == 0) sys->profile_frame_duration = stm_since(sys->profile_frame_start); sys->profile_step_duration += sys->profile_frame_duration; sys->profile_step_count += 1; sys->profile_frame_duration = 0; } /////////////////////////////////////////// void systems_step() { for (int32_t i = 0; i < systems.count; i++) system_execute(&systems[i]); } /////////////////////////////////////////// void systems_step_partial(system_run_ run_section, int32_t system_idx) { int32_t start, end; switch (run_section) { case system_run_before: { start = 0; end = system_idx; } break; case system_run_from: { start = system_idx; end = systems.count; } break; default: { start = 0; end = systems.count; } break; } for (int32_t i=start; i= 0; i--) { system_t* sys = &systems[system_init_order[i]]; if (sys->func_shutdown == nullptr) continue; // start timing uint64_t start = stm_now(); sys->func_shutdown(); // end timing sys->profile_shutdown_duration = stm_since(start); } log_info("Session Performance Report:"); log_info("<~BLK>______________________________________________________<~clr>"); log_info("<~BLK>|<~clr> <~YLW>System <~BLK>|<~clr> <~YLW>Initialize <~BLK>|<~clr> <~YLW>Step <~BLK>|<~clr> <~YLW>Shutdown <~BLK>|<~clr>"); log_info("<~BLK>|________________|____________|__________|___________|<~clr>"); for (int32_t i = 0; i < systems.count; i++) { char start_time [24]; char update_time [24]; char shutdown_time[24]; if (systems[i].profile_start_duration != 0) { double ms = stm_ms(systems[i].profile_start_duration); snprintf(start_time, sizeof(start_time), "%s%8.2f<~BLK>ms", ms>500?"<~RED>":"", ms); } else snprintf(start_time, sizeof(start_time), " "); if (systems[i].profile_step_duration != 0) { double ms = stm_ms(systems[i].profile_step_duration / systems[i].profile_step_count); // Exception for FramePresent, since it includes vsync time snprintf(update_time, sizeof(update_time), "%s%6.3f<~BLK>ms", ms>8 && !string_eq(systems[i].name, "FramePresent") ? "<~RED>":"", ms); } else snprintf(update_time, sizeof(update_time), " "); if (systems[i].profile_shutdown_duration != 0) { double ms = stm_ms(systems[i].profile_shutdown_duration); snprintf(shutdown_time, sizeof(shutdown_time), "%s%7.2f<~BLK>ms", ms>500?"<~RED>":"", ms); } else snprintf(shutdown_time, sizeof(shutdown_time), " "); log_infof("<~BLK>|<~CYN>%15s <~BLK>|<~clr> %s <~BLK>|<~clr> %s <~BLK>|<~clr> %s <~BLK>|<~clr>", systems[i].name, start_time, update_time, shutdown_time); } log_info("<~BLK>|________________|____________|__________|___________|<~clr>"); systems.free(); sk_free(system_init_order); } /////////////////////////////////////////// int32_t topological_sort(sort_dependency_t *dependencies, int32_t count, int32_t *ref_order) { // Topological sort, Depth-first algorithm: // https://en.wikipedia.org/wiki/Topological_sorting uint8_t *marks = sk_malloc_t(uint8_t, count); int32_t sorted_curr = count-1; memset(marks, 0, sizeof(uint8_t) * count); while (sorted_curr > 0) { for (int32_t i = 0; i < count; i++) { if (marks[i] != 0) continue; int result = topological_sort_visit(dependencies, count, i, marks, &sorted_curr, ref_order); // If we found a cyclic dependency, ditch out! if (result != 0) { sk_free(marks); return result; } } } sk_free(marks); return 0; } /////////////////////////////////////////// #define MARK_TEMP 1 #define MARK_PERM 2 int32_t topological_sort_visit(sort_dependency_t *dependencies, int32_t count, int32_t index, uint8_t *marks, int32_t *sorted_curr, int32_t *out_order) { if (marks[index] == MARK_PERM) return 0; if (marks[index] == MARK_TEMP) return index; marks[index] = MARK_TEMP; for (int32_t i = 0; i < count; i++) { for (int32_t d = 0; d < dependencies[i].count; d++) { if (dependencies[i].ids[d] == index) { int result = topological_sort_visit(dependencies, count, i, marks, sorted_curr, out_order); if (result != 0) return result; } } } marks[index] = MARK_PERM; out_order[*sorted_curr] = index; *sorted_curr = *sorted_curr-1; return 0; } } // namespace sk