#include #include #include #include #include "glm/gtx/transform.hpp" #include "spdlog/spdlog.h" App::App() {} void App::init(const AppParams& params) { m_params = params; AssetFS::GetInstance().Init(params.exeDir); // AssetFS::GetInstance().Mount("engine", params.exeDir / "assets" / "engine"); // AssetFS::GetInstance().Mount("game", params.exeDir / "assets" / "game"); window = SDL_CreateWindow( params.windowTitle.c_str(), // pos SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, // size params.windowSize.x, params.windowSize.y, SDL_WINDOW_VULKAN); SDL_SetWindowResizable(window, SDL_TRUE); if (!window) { spdlog::error("Failed to create window. SDL Error: {}", SDL_GetError()); std::exit(1); } gfxDevice.init(window, params.appName, false); InputManager::GetInstance().Init(); customInit(); } void App::run() { using clock = std::chrono::steady_clock; const float targetHz = 60.0f; const float dt = 1.0f / targetHz; const float maxFrameTime = 0.25f; // clamp big hitches const int maxSteps = 5; // prevent spiral of death auto prevTime = clock::now(); float accumulator = 0.0f; isRunning = true; while (isRunning) { const auto frameStart = clock::now(); float frameTimeSec = std::chrono::duration(frameStart - prevTime).count(); prevTime = frameStart; if (frameTimeSec > 0.07f && frameTimeSec < 5.f) { spdlog::warn("Frame drop detected, time: {:.4f}s", frameTimeSec); } if (frameTimeSec > maxFrameTime) frameTimeSec = maxFrameTime; if (frameTimeSec < 0.0f) frameTimeSec = 0.0f; accumulator += frameTimeSec; if (frameTimeSec > 0.0f) { const float newFPS = 1.0f / frameTimeSec; avgFPS = std::lerp(avgFPS, newFPS, 0.1f); } InputManager::GetInstance().BeginFrame(); camera.Update(dt); SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { isRunning = false; break; } if (event.type == SDL_WINDOWEVENT) { switch (event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_RESIZED: m_params.windowSize = { event.window.data1, event.window.data2 }; break; } } if (InputManager::GetInstance().ProcessEvent(event)) { isRunning = false; } } if (!isRunning) break; customUpdate(dt); // ---- Swapchain resize check once per frame ---- if (gfxDevice.needsSwapchainRecreate()) { spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y); gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y); onWindowResize(m_params.windowSize.x, m_params.windowSize.y); } // ---- Fixed updates (no event polling inside) ---- int steps = 0; while (accumulator >= dt && steps < maxSteps) { //Set window title to fps SDL_SetWindowTitle( window, fmt::format("{} - FPS: {:.2f}", m_params.windowTitle, avgFPS).c_str()); accumulator -= dt; steps++; } // If we hit the step cap, drop leftover time to recover smoothly if (steps == maxSteps) accumulator = 0.0f; // Optional interpolation factor for rendering const float alpha = accumulator / dt; // ---- Render ---- if (!gfxDevice.needsSwapchainRecreate()) { // glm::mat4 objMatrix = glm::translate(glm::mat4(1.f), glm::vec3(0.f, -3.0f, 0.f)); // // renderer.beginDrawing(gfxDevice); // renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID); // renderer.drawMesh(testMeshID, objMatrix, 0); // renderer.endDrawing(); // // const auto cmd = gfxDevice.beginFrame(); // const auto& drawImage = renderer.getDrawImage(gfxDevice); // // renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{ // camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f // }); // // gfxDevice.endFrame(cmd, drawImage, { // .clearColor = {{0.f, 0.f, 0.5f, 1.f}}, // .drawImageBlitRect = glm::ivec4{} // }); customDraw(); } // ---- Frame cap (if you still want it) ---- if (frameLimit) { const auto targetEnd = frameStart + std::chrono::duration_cast( std::chrono::duration(dt) ); std::this_thread::sleep_until(targetEnd); } } gfxDevice.waitIdle(); } void App::cleanup() { }