#include "system.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../libraries/stref.h"
#include "../libraries/array.h"
#include "../libraries/sokol_time.h"
#include "../libraries/profiler.h"
#include "../stereokit.h"
#include "../sk_memory.h"
namespace sk {
struct sort_dependency_t {
int32_t *ids;
int32_t count;
};
array_t<system_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;
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;
}
}
}
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);
}
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;
}
}
}
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);
}
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) continue;
log_diagf("Initializing %s", systems[index].name);
uint64_t start = stm_now();
if (!systems[index].func_initialize()) {
log_errf("System %s failed to initialize!", systems[index].name);
return false;
}
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;
sys->profile_frame_start = stm_now();
{
profiler_zone_text("%s", sys->name);
sys->func_step();
}
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_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<end; i++)
system_execute(&systems[i]);
}
void systems_shutdown() {
for (int32_t i = systems.count-1; i >= 0; i--) {
system_t* sys = &systems[system_init_order[i]];
if (sys->func_shutdown == nullptr) continue;
uint64_t start = stm_now();
sys->func_shutdown();
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);
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) {
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 (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;
}
}