#include #include #include #include #include #include #include "player-input.hpp" #include "asset-manager.hpp" #include "ecs.hpp" #include "entities.hpp" #include "game-settings.hpp" #include "game-type-defs.hpp" #include "game.hpp" #include "window.hpp" void signal_handler(int signal_num) { fprintf(stdout, "Received signal: %d - shutting down\n", signal_num); pkeSettings.isGameRunning = false; } PKEWindowProperties windowProps{}; void Tick(double delta) { Game_Tick(delta); } int main() { signal(SIGTERM, signal_handler); fprintf(stdout, "PKE ENTERING\n"); try { AM_Init(); Game_Init(); ECS_Init(); PkeInput_Init(); CreateWindow(&windowProps); EntityType_Init(); GameTimePoint lastTimePoint = pkeSettings.steadyClock.now(); double deltaTillNextRender = pkeSettings.deltaPerFrame; GameTimePoint lastLogTimePoint = pkeSettings.steadyClock.now(); int64_t tickCount = 0; int64_t renderCount = 0; int64_t nsAhead = 0.0; while (pkeSettings.isGameRunning) { glfwPollEvents(); int64_t nsAheadHolder = 0.0; if (nsAhead > 0) { nsAheadHolder = nsAhead; std::this_thread::sleep_for(GameTimeDuration(nsAhead)); nsAhead = 0; } if (vidMode.refreshRate != pkeSettings.targetFPS) { pkeSettings.targetFPS = vidMode.refreshRate; pkeSettings.deltaPerFrame = 1 / double(pkeSettings.targetFPS); } GameTimePoint currentTimePoint = pkeSettings.steadyClock.now(); double deltaThisTick = ((currentTimePoint - lastTimePoint).count() - nsAheadHolder) / NANO_DENOM_DOUBLE; deltaThisTick = std::min(deltaThisTick, pkeSettings.minimumDeltaPerFrame); lastTimePoint = currentTimePoint; deltaTillNextRender -= deltaThisTick; bool shouldRender = pkeSettings.graphicsSettings.isFramerateUnlocked || pkeSettings.graphicsSettings.isWaitingForVsync || deltaTillNextRender <= 0.0; if (shouldRender == false && (deltaTillNextRender > 0.0 && deltaTillNextRender - (deltaThisTick * 2.0) <= 0.0)) { /* * We are ahead of the render schedule * && the current tick's speed would put us behind schedule next tick. * Simulate the extra time we are ahead and prepare to sleep the difference * before the next tick. */ nsAhead = std::floor(deltaTillNextRender * NANO_DENOM_DOUBLE); deltaThisTick += deltaTillNextRender; shouldRender = true; } tickCount += 1; Tick(deltaThisTick); if (shouldRender) { Render(); renderCount += 1; double msBehind = deltaTillNextRender * -1000; int64_t behindCount = 0; while (deltaTillNextRender < pkeSettings.deltaPerFrame) { behindCount += 1; deltaTillNextRender += pkeSettings.deltaPerFrame; } if (behindCount > 2) { fprintf(stderr, "[PKE::main] late render - simulated ahead: %fms - delta behind: %fms - missed frames:%ld\n", nsAheadHolder / (NANO_DENOM_DOUBLE / 1000), msBehind, behindCount - 1); fflush(stderr); } } if ((currentTimePoint - lastLogTimePoint).count() > std::chrono::nanoseconds::period::den) { lastLogTimePoint = currentTimePoint; fprintf(stdout, "TPS: ~%ld - actual:%ld - presents:%ld\n", int64_t(1 / deltaThisTick), tickCount, renderCount); fflush(stdout); tickCount = 0; renderCount = 0; } pkeSettings.isGameRunning = !glfwWindowShouldClose(window); } vkDeviceWaitIdle(vkDevice); } catch (const std::exception &exc) { fprintf(stdout, "EXCEPTION: %s\n", exc.what()); } catch (const char *err) { fprintf(stdout, "UNHANDLED EXCEPTION: %s\n", err); } catch (...) { fprintf(stdout, "UNHANDLED EXCEPTION\n"); } fprintf(stdout, "PKE SHUTDOWN INITIATED\n"); Game_Teardown(); Event_Teardown(); EntityType_Teardown(); PkeInput_Teardown(); ECS_Teardown(); AM_DebugPrint(); AM_Teardown(); DestroyWindow(); Pke_DebugPrint(); fprintf(stdout, "PKE EXITING\n"); return 0; }