/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "GraphicsAppManager.hpp" #include "Base/IPlatform.hpp" #include "Base/Logger.hpp" #include "Base/FrameMetadata.hpp" #include "PlatformProducer.hpp" #include "Input/InputManager.hpp" #include "Base/EngineConfiguration.hpp" #include #include #include #include namespace OpenVulkano { using clock = std::chrono::steady_clock; GraphicsAppManager::GraphicsAppManager(OpenVulkano::IGraphicsApp* app, RenderAPI::RenderApi renderApi) : app(app), renderApi(renderApi) { Utils::SetThreadName("Main"); ZoneScoped; Logger::SetupLogger(); if (!app) { constexpr auto msg = "The app must not be null!"; Logger::MANAGER->error(msg); throw std::runtime_error(msg); } platform = std::unique_ptr(PlatformProducer::CreatePlatform(renderApi)); window = platform->MakeWindow(); renderer = std::unique_ptr(PlatformProducer::CreateRenderManager(renderApi)); app->SetGraphicsAppManager(this); window->SetWindowHandler(this); inputManager = Input::InputManager::GetInstance(); engineConfig = EngineConfiguration::GetEngineConfiguration(); engineConfig->OnFpsCapChanged += EventHandler(this, &GraphicsAppManager::UpdateCappedFpsInfo); // set initial values if (engineConfig->GetFpsCap() > 0) { UpdateCappedFpsInfo(engineConfig->GetFpsCap()); } } GraphicsAppManager::GraphicsAppManager(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi) : app(app), renderApi(renderApi) { Utils::SetThreadName("Main"); ZoneScoped; Logger::SetupLogger(); if (!app) { constexpr auto msg = "The app must not be null!"; Logger::MANAGER->error(msg); throw std::runtime_error(msg); } //platform = std::unique_ptr(PlatformProducer::CreatePlatform(renderApi)); this->window = window; renderer = std::unique_ptr(PlatformProducer::CreateRenderManager(renderApi)); app->SetGraphicsAppManager(this); window->SetWindowHandler(this); inputManager = OpenVulkano::Input::InputManager::GetInstance(); engineConfig = OpenVulkano::EngineConfiguration::GetEngineConfiguration(); engineConfig->OnFpsCapChanged += EventHandler(this, &GraphicsAppManager::UpdateCappedFpsInfo); // set initial values if (engineConfig->GetFpsCap() > 0) { UpdateCappedFpsInfo(engineConfig->GetFpsCap()); } } void GraphicsAppManager::UpdateCappedFpsInfo(int32_t newFpsCap) { if (newFpsCap < 0) { cappedFrameTime = std::chrono::microseconds(0); } else { cappedFrameTime = std::chrono::microseconds(1'000'000 / newFpsCap); } } void GraphicsAppManager::OnCappedFPS(const auto& frameStartTime) { auto frameEndTime = frameStartTime + cappedFrameTime; while (clock::now() < frameEndTime) { std::this_thread::yield(); } } GraphicsAppManager::~GraphicsAppManager() noexcept { if (windowTitleFormat.empty()) return; engineConfig->OnFpsCapChanged -= EventHandler(this, &GraphicsAppManager::UpdateCappedFpsInfo); ShutDown(); } void GraphicsAppManager::Stop() { running = false; Logger::MANAGER->info("Graphics application stopped"); } void GraphicsAppManager::Pause() { paused = true; frameTimer.Stop(); Logger::MANAGER->info("Graphics application paused"); } void GraphicsAppManager::Resume() { paused = false; frameTimer.Start(); Logger::MANAGER->info("Graphics application resumed"); } void GraphicsAppManager::Run() { running = true; StartUp(); frameTimer.Reset(); Loop(); // Runs the rendering loop ShutDown(); } void GraphicsAppManager::StartUp() { ZoneScoped; try { Logger::MANAGER->info("Initializing ..."); app->Init(); if (platform) platform->Init(); window->Init(renderApi); //TODO restore window settings if there are any set renderer->Init(static_cast(this), window); windowTitleFormat = app->GetAppName() + " " + static_cast(app->GetAppVersion()) + " - " + renderer->GetMainRenderDeviceName() + " - {:.1f} fps ({:.1f} ms)"; app->InitPostGraphics(); Logger::MANAGER->info("Initialized"); } catch (std::exception& e) { Logger::MANAGER->error("Failed to initiate: {0}", e.what()); running = false; #ifdef DEBUG throw e; #endif } } void GraphicsAppManager::Loop() { while (running) { LoopTick(); } } void GraphicsAppManager::LoopTick() { FrameMark; ZoneScoped; if (platform) platform->Tick(); if (paused) { // The rendering is paused // No need to burn cpu time if the app is paused std::this_thread::sleep_for(std::chrono::milliseconds(50)); } else { auto start = clock::now(); inputManager->Tick(); app->Tick(); if (CURRENT_FRAME.needsRedraw) renderer->Tick(); if (cappedFrameTime.count()) { OnCappedFPS(start); } frameTimer.Tick(); UpdateFps(); CURRENT_FRAME.frameId = frameCount; CURRENT_FRAME.frameTime = frameTimer.GetTickSeconds(); CURRENT_FRAME.needsRedraw = !EngineConfiguration::GetEngineConfiguration()->GetLazyRendering(); } } void GraphicsAppManager::ShutDown() { ZoneScoped; Logger::MANAGER->info("Shutting down ..."); app->Close(); renderer->Close(); window->Close(); if (platform) platform->Close(); windowTitleFormat = ""; app->CloseFinalize(); Logger::MANAGER->info("Shutdown complete"); } void GraphicsAppManager::UpdateFps() { frameCount++; fpsTimer += frameTimer.GetTickSeconds(); if(fpsTimer > 1.0f) { avgFps = static_cast(frameCount - lastFrameCount) / fpsTimer; avgFrameTime = (1 / avgFps) * 1000.0f; lastFrameCount = frameCount; fpsTimer = 0; if (window->HasTitle()) { window->SetTitle(fmt::format(fmt::runtime(windowTitleFormat), avgFps, avgFrameTime)); } } } void GraphicsAppManager::OnWindowMinimize(OpenVulkano::IWindow* window) { if (window != this->window) return; Pause(); } void GraphicsAppManager::OnWindowRestore(OpenVulkano::IWindow* window) { if (window != this->window) return; Resume(); CURRENT_FRAME.needsRedraw = true; } void GraphicsAppManager::OnWindowFocusLost(IWindow* window) {} void GraphicsAppManager::OnWindowFocusGained(IWindow* window) {} void GraphicsAppManager::OnWindowMove(IWindow* window, int posX, int posY) { //TODO save window pos } void GraphicsAppManager::OnWindowResize(OpenVulkano::IWindow* window, const uint32_t newWidth, const uint32_t newHeight) { if(window != this->window) return; if (window->GetWidth() != newWidth || window->GetHeight() != newHeight) window->SetSize(newWidth, newHeight); renderer->Resize(newWidth, newHeight); CURRENT_FRAME.needsRedraw = true; } void GraphicsAppManager::OnWindowClose(OpenVulkano::IWindow* window) { window->SetWindowHasBeenDestroyed(); if (window != this->window) return; Stop(); } }