#include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) /* include winapi */ #include #elif defined(__APPLE__) /* mac includes */ #elif defined(__linux) || defined(__linux__) || defined(linux) #include #include #endif #include #include #include #include #include #include #include #include #include #include #define DEFAULT_NUM_PROCS 8 Instance* GLOBAL_PLATFORM = NULL; static Camera default_camera = { .pos = {3, 0, 0}, .dir = {1, 1, 1}, }; input_callback_t* callbacks[128]; usize callbacks_len; i32 nproc(void) { return get_nprocs(); } void delay( uint64_t us ) { #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) Sleep( ms ); #else struct timespec ts = { .tv_sec = (i64)us / 1000000, .tv_nsec = ((i64)us * 1000) % 1000000000 }; struct timespec rem = {0,0}; int err = 0; while((err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &rem))) { switch (err) { case EINTR: WARN("clock_nanosleep didn't sleep full duration: interrupted."); break; case EINVAL: WARN("clock_nanosleep didn't sleep full duration: invalid argument."); break; case ENOTSUP: WARN("clock_nanosleep didn't sleep full duration: unsupported clock."); break; default: WARN("clock_nanosleep didn't sleep full duration!"); break; } ts = rem; rem = (struct timespec){0,0}; } #endif } i32 cmp_int(const void* a, const void* b) { const i32* x = a; const i32* y = b; return *x - *y; } /* Creates the window, initializes IO, Rendering, Fonts and engine-specific * resources. */ Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, const u32 flags, const usize initial_memory) { INFO("Engine version " ENGINE_VERSION); #if defined(__linux) || defined(__linux__) || defined(linux) { pid_t pid = getpid(); INFO("Starting with pid %lu", pid); } #endif ivec2 windowsize = {windowWidth, windowHeight}; Instance* p = (Instance*)calloc(1, sizeof(Instance)); Window* w = (Window*)calloc(1, sizeof(Window)); /* initialize resources */ Resources* resources = calloc(1, sizeof(Resources)); // TODO: Initialize them :) w = Window_new(&Platform_GLFW, windowtitle, WINDOW_FRAMEWORK_GLFW, WINDOW_RENDERER_OPENGL, (ivec2){windowsize[0], windowsize[1]}, flags); p->window = w; p->quit = false; p->resources = resources; p->frame = 0; #ifdef _DEBUG engine_fps_max(p, 30); #else engine_fps_max(p, 300); #endif p->mem = memory_new(initial_memory); p->cam = &default_camera; glm_ortho_default(45.f, p->cam->per); // TODO: Add global bindings INFO("Available cores: %d", nproc()); GLOBAL_PLATFORM = p; #ifdef DAW_BUILD_HOTRELOAD #define State(name) \ if (!State_reload(STATE_##name, p->window->bindings, p->window->bindings_len)) { \ ERROR("Failed to reload shared object file for state %s", #name); \ }; #include #undef State #endif return p; } i32 engine_run(Instance* p, StateType initial_state, void* state_arg) { if (p == NULL) { ERROR("Platform is uninitialized.\n"); INFO("initialize with `engine_init`"); return -1; } memory* mem = p->mem; StateType state = initial_state; { u64 state_init_time = get_time(); State_init(state, mem, state_arg); INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], (get_time() - state_init_time)); } u64 frame_end = get_time(); // Update ticks u64 ticks = 0; StateType (*update_func)(f64, void*) = State_updateFunc(state); u64 last_fps_measurement = frame_end; u64 last_fps_ticks = 0; /* The target frametime measured in μs */ const u32 fps_cap = p->fps_target > 0 ? 1000000 / p->fps_target : 0; /* Main loop */ do { /* frame_start is μs since engine was initialized */ const u64 frame_start = get_time(); /* dt measured in μs */ const u64 dt = frame_start - frame_end; /* Delta is relative to FPS cap */ //const f64 delta = (f64)dt / (f64)fps_cap; /* Events */ Platform_GLFW.window_poll(); i_flush_bindings(dt, callbacks_len, callbacks, mem->data); /* Update */ StateType next_state; next_state = update_func(dt, (void*)(mem->data)); if (next_state != STATE_null) { if (next_state == STATE_quit) break; drawcall_reset(); void* retval = State_free(state, mem); memory_clear(mem); i_ctx_reset(); // Reset camera to default camera p->cam = &default_camera; state = next_state; update_func = State_updateFunc(state); { u64 state_init_time = get_time(); State_init(state, mem, retval); INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], (get_time() - state_init_time)); } } else { /* Render */ const u64 rendertime_begin = get_time(); render_begin(p->window); render_present(p->window); const u64 rendertime_dt = get_time() - rendertime_begin; /* Regulate FPS */ frame_end = get_time(); const i64 fps_diff = fps_cap - (i64)(frame_end - frame_start); if (fps_diff > 0) { delay((u64)fps_diff); } frame_end = frame_start; /* Print stats */ const u64 dt_measurement = frame_end - last_fps_measurement; /* only make measurements once a second */ if (dt_measurement > 1000000) { printf(" FPS: %.1f" "\tticks: %lu" "\tframetime: %lu.%03lums" "\trendertime: %lu.%03lums" "\n" , ((f64)(ticks - last_fps_ticks)) / ((f64)dt_measurement / 1000000.0) , ticks , dt / 1000 , dt % 1000 , rendertime_dt / 1000 , rendertime_dt % 1000 ); last_fps_measurement = frame_end; last_fps_ticks = ticks; } } ticks++; } while( !Platform_GLFW.window_should_close(p->window) && state != STATE_quit ); return 0; } void engine_stop(Instance* p) { if (p == NULL) return; //{ /* Deallocate resources */ // struct Resources* r = (struct Resources*)p->data; // if (r != NULL) { // /* Destroy textures */ // for (usize i = 0; i < r->textures_len; i++) { // if (r->textures[i] != NULL) { // r->textures[i] = NULL; // } // } // free(r->textures); // /* Destroy Fonts */ // } //} Platform_GLFW.window_destroy(p->window); free(p->resources); } /* Set the maximum framerate */ void engine_fps_max(Instance* p, u16 cap) { LOG("Setting max fps to %llu", cap); p->fps_target = cap; } usize f_get_sz(FILE* f) { if (f == NULL) { ERROR("File was null!"); return 0; } const isize pos = ftell(f); if (pos == -1) { ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); return 0; } const i32 err = fseek(f, 0, SEEK_END); if (err != 0) { if (err == ESPIPE) { ERROR("File is a pipe"); } else { ERROR("Failed to determine file size!"); } return 0; } const isize size = ftell(f); if (size == -1) { ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); return 0; } // Reset the position to the position prior to calling f_get_sz fseek(f, pos, SEEK_SET); return (usize)size; }