#include "application.h" #include #include #include #include #include #include #include "gfx.h" #include "gfx_device.h" #include "input_manager.h" #include "log.h" #include "resources/font.h" #include "resources/material.h" #include "resources/mesh.h" #include "resources/shader.h" #include "resources/texture.h" #include "scene.h" #include "scene_manager.h" #include "window.h" #ifdef _MSC_VER #include #include #define WIN_MAX_PATH 260 #endif namespace engine { static std::filesystem::path getResourcesPath() { std::filesystem::path resourcesPath{}; #ifdef _MSC_VER CHAR exeDirBuf[MAX_PATH + 1]; GetModuleFileNameA(NULL, exeDirBuf, WIN_MAX_PATH + 1); std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); #else std::filesystem::path cwd = std::filesystem::current_path(); #endif if (std::filesystem::is_directory(cwd / "res")) { resourcesPath = cwd / "res"; } else { resourcesPath = cwd.parent_path() / "share" / "sdltest"; } if (std::filesystem::is_directory(resourcesPath) == false) { resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest"; } if (std::filesystem::is_directory(resourcesPath) == false) { throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); } return resourcesPath; } Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings) { window_ = std::make_unique(appName, true, false); input_manager_ = std::make_unique(window_.get()); scene_manager_ = std::make_unique(this); // get base path for resources resources_path_ = getResourcesPath(); // register resource managers RegisterResourceManager(); RegisterResourceManager(); RegisterResourceManager(); RegisterResourceManager(); RegisterResourceManager(); // initialise the render data render_data_.gfxdev = std::make_unique(appName, appVersion, window_->GetHandle(), graphicsSettings); std::vector globalSetBindings; { auto& binding0 = globalSetBindings.emplace_back(); binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer; binding0.stage_flags = gfx::ShaderStageFlags::kVertex; } render_data_.global_set_layout = gfxdev()->CreateDescriptorSetLayout(globalSetBindings); render_data_.global_set = gfxdev()->AllocateDescriptorSet(render_data_.global_set_layout); RenderData::GlobalSetUniformBuffer globalSetUniformBufferData{ .proj = glm::mat4{ 1.0f }, }; render_data_.global_set_uniform_buffer = gfxdev()->CreateUniformBuffer(sizeof(RenderData::GlobalSetUniformBuffer), &globalSetUniformBufferData); gfxdev()->UpdateDescriptorUniformBuffer(render_data_.global_set, 0, render_data_.global_set_uniform_buffer, 0, sizeof(RenderData::GlobalSetUniformBuffer)); std::vector frameSetBindings; { auto& binding0 = frameSetBindings.emplace_back(); binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer; binding0.stage_flags = gfx::ShaderStageFlags::kVertex; } render_data_.frame_set_layout = gfxdev()->CreateDescriptorSetLayout(frameSetBindings); render_data_.frame_set = gfxdev()->AllocateDescriptorSet(render_data_.frame_set_layout); RenderData::FrameSetUniformBuffer initialSetOneData{ .view = glm::mat4{ 1.0f }, }; render_data_.frame_set_uniform_buffer = gfxdev()->CreateUniformBuffer(sizeof(RenderData::FrameSetUniformBuffer), &initialSetOneData); gfxdev()->UpdateDescriptorUniformBuffer(render_data_.frame_set, 0, render_data_.frame_set_uniform_buffer, 0, sizeof(RenderData::FrameSetUniformBuffer)); std::vector materialSetBindings; { auto& binding0 = materialSetBindings.emplace_back(); binding0.descriptor_type = gfx::DescriptorType::kCombinedImageSampler; binding0.stage_flags = gfx::ShaderStageFlags::kFragment; } render_data_.material_set_layout = gfxdev()->CreateDescriptorSetLayout(materialSetBindings); // default resources { auto monoFont = std::make_unique( GetResourcePath("engine/fonts/mono.ttf") ); GetResourceManager()->AddPersistent("builtin.mono", std::move(monoFont)); } { resources::Shader::VertexParams vertParams{}; vertParams.has_normal = true; vertParams.has_uv0 = true; auto texturedShader = std::make_unique( &render_data_, GetResourcePath("engine/shaders/standard.vert").c_str(), GetResourcePath("engine/shaders/standard.frag").c_str(), vertParams, false, true ); GetResourceManager()->AddPersistent("builtin.standard", std::move(texturedShader)); } { resources::Shader::VertexParams vertParams{}; vertParams.has_normal = true; vertParams.has_uv0 = true; auto skyboxShader = std::make_unique( &render_data_, GetResourcePath("engine/shaders/skybox.vert").c_str(), GetResourcePath("engine/shaders/skybox.frag").c_str(), vertParams, false, true ); GetResourceManager()->AddPersistent("builtin.skybox", std::move(skyboxShader)); } { auto whiteTexture = std::make_unique( &render_data_, GetResourcePath("engine/textures/white.png"), resources::Texture::Filtering::kOff ); GetResourceManager()->AddPersistent("builtin.white", std::move(whiteTexture)); } } Application::~Application() { for (const auto& [info, sampler] : render_data_.samplers) { gfxdev()->DestroySampler(sampler); } gfxdev()->DestroyDescriptorSetLayout(render_data_.material_set_layout); gfxdev()->DestroyUniformBuffer(render_data_.frame_set_uniform_buffer); gfxdev()->DestroyDescriptorSetLayout(render_data_.frame_set_layout); gfxdev()->DestroyUniformBuffer(render_data_.global_set_uniform_buffer); gfxdev()->DestroyDescriptorSetLayout(render_data_.global_set_layout); } void Application::GameLoop() { LOG_TRACE("Begin game loop..."); constexpr int FPS_LIMIT = 240; constexpr auto FRAMETIME_LIMIT = std::chrono::nanoseconds(1000000000 / FPS_LIMIT); auto beginFrame = std::chrono::steady_clock::now(); auto endFrame = beginFrame + FRAMETIME_LIMIT; auto lastTick = window_->GetNanos(); // single-threaded game loop while (window_->IsRunning()) { /* logic */ scene_manager_->UpdateActiveScene(window_->dt()); if(window_->GetKeyPress(inputs::Key::K_F)) [[unlikely]] { window_->InfoBox("fps", std::to_string(window_->GetFPS()) + " fps " + std::to_string(window_->dt() * 1000.0f) + " ms"); } uint64_t now = window_->GetNanos(); if (now - lastTick >= 1000000000LL * 5LL) [[unlikely]] { lastTick = now; LOG_INFO("fps: {}", window_->GetAvgFPS()); gfxdev()->LogPerformanceInfo(); window_->ResetAvgFPS(); } /* poll events */ window_->GetInputAndEvents(); /* fps limiter */ if (enable_frame_limiter_) { std::this_thread::sleep_until(endFrame); } beginFrame = endFrame; endFrame = beginFrame + FRAMETIME_LIMIT; } gfxdev()->WaitIdle(); } }