diff options
| author | hachem <im@hachem.wtf> | 2026-09-22 06:45:33 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-22 06:45:33 +0200 |
| commit | 32ee3d9f57b37723ee97c3b798be6968f98df1b1 (patch) | |
| tree | 10838a06f522a48f64645e2d31c3d72eb1a58643 | |
| parent | e321beb543311dcb591942ec42464e38cbeae0f0 (diff) | |
fix: windows support
| -rw-r--r-- | src/core/application.cpp | 50 | ||||
| -rw-r--r-- | src/core/application.h | 3 | ||||
| -rw-r--r-- | src/core/log.h | 9 | ||||
| -rw-r--r-- | src/core/window.cpp | 8 | ||||
| -rw-r--r-- | src/core/window.h | 2 | ||||
| -rw-r--r-- | src/main.cpp | 19 | ||||
| -rw-r--r-- | src/platform/opengl/shader.cpp | 13 | ||||
| -rw-r--r-- | src/platform/opengl/texture.cpp | 13 | ||||
| -rw-r--r-- | src/ui/ui_layer.cpp | 13 | ||||
| -rw-r--r-- | src/ui/ui_layer.h | 3 |
10 files changed, 105 insertions, 28 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp index e406511..ecaaa56 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -4,10 +4,13 @@ #include "settings_manager.h" #include "platform/opengl/opengl_device.h" -#include "platform/vulkan/vulkan_device.h" +#ifndef DONUT_NO_VULKAN + #include "platform/vulkan/vulkan_device.h" +#endif #include <GLFW/glfw3.h> #include <imgui.h> +#include <imgui_impl_glfw.h> #include <glm/glm.hpp> #include <algorithm> #include <thread> @@ -27,6 +30,7 @@ namespace Donut } Application::Application(const std::string& name, int width, int height) + : m_title(name), m_init_w(width), m_init_h(height) { s_instance = this; @@ -38,11 +42,30 @@ namespace Donut ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL); } +#ifdef DONUT_NO_VULKAN + if (RendererAPI::get_api() == RendererAPI::API::Vulkan) + { + DONUT_WARN("built without the Vulkan backend, falling back to OpenGL"); + RendererAPI::set_api(RendererAPI::API::OpenGL); + } +#else + if (RendererAPI::get_api() == RendererAPI::API::Vulkan && !RHI::vulkan_runtime_available()) + { + DONUT_WARN("no Vulkan runtime on this machine (vulkan-1.dll), falling back to OpenGL"); + RendererAPI::set_api(RendererAPI::API::OpenGL); + } if (RendererAPI::get_api() == RendererAPI::API::Vulkan) RHI::vulkan_prepare_glfw(); +#endif m_window = create_scope<Window>(name, width, height); m_window->set_event_callback([this](Event& event) { on_event(event); }); + if (!m_window->get_native_window()) + { + DONUT_ERROR("no window, no app (GLFW said why above)"); + m_running = false; + return; + } on_init(); } @@ -58,15 +81,31 @@ namespace Donut int w = 0, h = 0; glfwGetFramebufferSize((GLFWwindow*)m_window->get_native_window(), &w, &h); +#ifndef DONUT_NO_VULKAN if (RendererAPI::get_api() == RendererAPI::API::Vulkan) m_device = RHI::create_vulkan_device(); else +#endif m_device = RHI::create_opengl_device(); RHI::NativeWindow native{ m_window->get_native_window(), w, h }; if (!m_device->init(native)) { + m_device->shutdown(); + m_device.reset(); +#ifndef DONUT_NO_VULKAN + if (RendererAPI::get_api() == RendererAPI::API::Vulkan) + { + DONUT_WARN("Vulkan device init failed (see above), falling back to OpenGL"); + RendererAPI::set_api(RendererAPI::API::OpenGL); + m_window.reset(); + m_window = create_scope<Window>(m_title, m_init_w, m_init_h); + m_window->set_event_callback([this](Event& event) { on_event(event); }); + if (m_window->get_native_window()) { on_init(); return; } + } +#endif DONUT_ERROR("RHI device initialization failed"); + m_running = false; return; } @@ -76,7 +115,8 @@ namespace Donut m_ui = create_scope<UILayer>(); m_device->init_imgui(); - m_ui->init(); + const float dpi = ImGui_ImplGlfw_GetContentScaleForWindow((GLFWwindow*)m_window->get_native_window()); + m_ui->init(dpi); g_prev_scroll = glfwSetScrollCallback((GLFWwindow*)m_window->get_native_window(), donut_scroll_callback); @@ -93,8 +133,8 @@ namespace Donut { double frame_start = glfwGetTime(); glfwPollEvents(); - if (!m_minimized && m_device) - render_frame(); + if (m_minimized) { glfwWaitEventsTimeout(0.1); continue; } + if (m_device) render_frame(); if (!SettingsManager::get_v_sync_enabled()) { @@ -165,7 +205,7 @@ namespace Donut { ExportConfig cfg; cfg.width = 1280; cfg.height = 720; - int written = export_frame(cfg); + [[maybe_unused]] const int written = export_frame(cfg); DONUT_INFO("autoexport wrote {} file(s)", written); close(); } diff --git a/src/core/application.h b/src/core/application.h index 5a4957b..1fc65b9 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -62,6 +62,9 @@ namespace Donut bool m_running = true; bool m_minimized = false; + std::string m_title; + int m_init_w = 1280, m_init_h = 720; + static Application* s_instance; }; } diff --git a/src/core/log.h b/src/core/log.h index 4237d1c..cc99aa1 100644 --- a/src/core/log.h +++ b/src/core/log.h @@ -144,13 +144,10 @@ namespace Donut #if defined(DONUT_DEBUG) #define DONUT_TRACE(format, ...) ::Donut::Logger::trace(format __VA_OPT__(,) __VA_ARGS__) #define DONUT_INFO(format, ...) ::Donut::Logger::info(format __VA_OPT__(,) __VA_ARGS__) - #define DONUT_WARN(format, ...) ::Donut::Logger::warn(format __VA_OPT__(,) __VA_ARGS__) - #define DONUT_ERROR(format, ...) ::Donut::Logger::error(format __VA_OPT__(,) __VA_ARGS__) - #define DONUT_FATAL(format, ...) ::Donut::Logger::fatal(format __VA_OPT__(,) __VA_ARGS__) #else #define DONUT_TRACE(format, ...) {} #define DONUT_INFO(format, ...) {} - #define DONUT_WARN(format, ...) {} - #define DONUT_ERROR(format, ...) {} - #define DONUT_FATAL(format, ...) {} #endif +#define DONUT_WARN(format, ...) ::Donut::Logger::warn(format __VA_OPT__(,) __VA_ARGS__) +#define DONUT_ERROR(format, ...) ::Donut::Logger::error(format __VA_OPT__(,) __VA_ARGS__) +#define DONUT_FATAL(format, ...) ::Donut::Logger::fatal(format __VA_OPT__(,) __VA_ARGS__) diff --git a/src/core/window.cpp b/src/core/window.cpp index d3c1ff5..090fc47 100644 --- a/src/core/window.cpp +++ b/src/core/window.cpp @@ -24,6 +24,8 @@ namespace Donut { DONUT_INFO("Initializing window: ", m_title, " (", m_width, "x", m_height, ")"); + glfwSetErrorCallback(glfw_error_callback); + if (!s_glfw_initialized) { int success = glfwInit(); @@ -43,19 +45,21 @@ namespace Donut } else { -#ifdef __APPLE__ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); #endif } + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); m_window = glfwCreateWindow(m_width, m_height, m_title.c_str(), nullptr, nullptr); if (!m_window) { DONUT_ERROR("Could not create GLFW window!"); glfwTerminate(); + s_glfw_initialized = false; return; } @@ -65,7 +69,6 @@ namespace Donut glfwMakeContextCurrent(m_window); glfwSetWindowUserPointer(m_window, this); - glfwSetErrorCallback(glfw_error_callback); glfwSetWindowCloseCallback(m_window, glfw_window_close_callback); glfwSetWindowSizeCallback(m_window, glfw_window_size_callback); glfwSetWindowFocusCallback(m_window, glfw_window_focus_callback); @@ -82,6 +85,7 @@ namespace Donut auto Window::shutdown() -> void { DONUT_INFO("Shutting down window: ", m_title); + if (!m_window) return; glfwDestroyWindow(m_window); s_glfw_window_count--; diff --git a/src/core/window.h b/src/core/window.h index b8b59c5..b97874d 100644 --- a/src/core/window.h +++ b/src/core/window.h @@ -53,7 +53,7 @@ namespace Donut static auto glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos) -> void; private: - GLFWwindow* m_window; + GLFWwindow* m_window = nullptr; EventHandler m_event_handler; std::string m_title; diff --git a/src/main.cpp b/src/main.cpp index 323d141..3d5b685 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,24 @@ #include "core/application.h" -int main() +#include <filesystem> + +static auto chdir_to_project_root(std::filesystem::path start) -> bool +{ + namespace fs = std::filesystem; + std::error_code ec; + for (fs::path p = start; !p.empty(); p = p.parent_path()) + { + if (fs::exists(p / "assets" / "shaders", ec)) { fs::current_path(p, ec); return !ec; } + if (p == p.root_path()) break; + } + return false; +} + +int main(int, char** argv) { + std::error_code ec; + if (!chdir_to_project_root(std::filesystem::current_path(ec)) && argv && argv[0]) + chdir_to_project_root(std::filesystem::absolute(argv[0], ec).parent_path()); Donut::Application* app = new Donut::Application("Donut Engine - Black Hole Simulation", 1280, 720); app->run(); delete app; diff --git a/src/platform/opengl/shader.cpp b/src/platform/opengl/shader.cpp index b815021..21335dd 100644 --- a/src/platform/opengl/shader.cpp +++ b/src/platform/opengl/shader.cpp @@ -6,6 +6,8 @@ #include <fstream> #include <iostream> #include <vector> +#include <filesystem> +#include "core/log.h" namespace Donut { @@ -89,6 +91,13 @@ namespace Donut in.read(&result[0], size); } } + else + { + std::error_code ec; + const auto cwd = std::filesystem::current_path(ec).u8string(); + DONUT_ERROR("shader file not found: {} (cwd {}; generated shaders live in assets/shaders/generated)", + filepath, std::string(cwd.begin(), cwd.end())); + } return result; } @@ -136,7 +145,7 @@ namespace Donut int max_length = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); std::vector<char> info_log(max_length); - glGetShaderInfoLog(shader, max_length, &max_length, &info_log[0]); + if (max_length > 0) glGetShaderInfoLog(shader, max_length, &max_length, info_log.data()); glDeleteShader(shader); for (auto id : glShaderIDs) glDeleteShader(id); @@ -160,7 +169,7 @@ namespace Donut int max_length = 0; glGetProgramiv(m_renderer_id, GL_INFO_LOG_LENGTH, &max_length); std::vector<char> info_log(max_length); - glGetProgramInfoLog(m_renderer_id, max_length, &max_length, &info_log[0]); + if (max_length > 0) glGetProgramInfoLog(m_renderer_id, max_length, &max_length, info_log.data()); glDeleteProgram(m_renderer_id); for (auto id : glShaderIDs) glDeleteShader(id); diff --git a/src/platform/opengl/texture.cpp b/src/platform/opengl/texture.cpp index 11ae1e3..5cd7e74 100644 --- a/src/platform/opengl/texture.cpp +++ b/src/platform/opengl/texture.cpp @@ -180,15 +180,14 @@ namespace Donut glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - auto equirect_shader = Shader::create("assets/shaders/equirect_to_cubemap.glsl"); - if (!equirect_shader) + auto equirect_shader = Shader::create("assets/shaders/generated/equirect_to_cubemap.glsl"); + uint32_t shader_program = equirect_shader ? equirect_shader->get_renderer_id() : 0; + if (!shader_program) { - DONUT_ERROR("Failed to create equirectangular to cubemap shader"); + DONUT_ERROR("equirect_to_cubemap shader missing or failed to compile; the sky stays dark"); return; } - uint32_t shader_program = equirect_shader->get_renderer_id(); - float vertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, @@ -221,7 +220,7 @@ namespace Donut glUseProgram(shader_program); glUniform1i(glGetUniformLocation(shader_program, "u_EquirectangularMap"), 0); - glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_Projection"), 1, GL_TRUE, &capture_projection[0][0]); + glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_projection"), 1, GL_TRUE, &capture_projection[0][0]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, hdr_texture); @@ -229,7 +228,7 @@ namespace Donut glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); for (unsigned int i = 0; i < 6; ++i) { - glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_View"), 1, GL_TRUE, &capture_views[i][0][0]); + glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_view"), 1, GL_TRUE, &capture_views[i][0][0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_renderer_id, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindVertexArray(cube_vao); diff --git a/src/ui/ui_layer.cpp b/src/ui/ui_layer.cpp index 69e2e41..6cbdc9d 100644 --- a/src/ui/ui_layer.cpp +++ b/src/ui/ui_layer.cpp @@ -121,7 +121,7 @@ namespace Donut out->append("\n"); } - static auto apply_donut_style() -> void + static auto apply_donut_style(float dpi_scale) -> void { ImGuiStyle& s = ImGui::GetStyle(); s.WindowRounding = 8.0f; s.ChildRounding = 6.0f; s.FrameRounding = 6.0f; @@ -197,6 +197,8 @@ namespace Donut c[ImGuiCol_TableRowBgAlt] = ImVec4(1.0f, 1.0f, 1.0f, 0.025f); c[ImGuiCol_TextSelectedBg] = amber_dim; c[ImGuiCol_DragDropTarget] = kAmberHi; + + if (dpi_scale != 1.0f) { s.ScaleAllSizes(dpi_scale); s.FontScaleDpi = dpi_scale; } } static auto prop(const char* label, float column = 116.0f) -> void @@ -238,8 +240,9 @@ namespace Donut } } - auto UILayer::init() -> void + auto UILayer::init(float dpi_scale) -> void { + m_dpi_scale = dpi_scale; ImGuiIO& io = ImGui::GetIO(); m_font_body = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Regular.ttf", kBodySize); m_font_heading = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-SemiBold.ttf", kHeadSize); @@ -302,7 +305,7 @@ namespace Donut auto UILayer::draw(const UIContext& ctx) -> View { static bool styled = false; - if (!styled) { apply_donut_style(); styled = true; } + if (!styled) { apply_donut_style(m_dpi_scale); styled = true; } ImGuizmo::BeginFrame(); @@ -909,7 +912,11 @@ namespace Donut }; char buf[64]; row("Device", ctx.device_name.empty() ? "GPU" : ctx.device_name.c_str()); +#ifdef __APPLE__ row("API", RendererAPI::get_api() == RendererAPI::API::Vulkan ? "Vulkan (MoltenVK)" : "OpenGL 4.1"); +#else + row("API", RendererAPI::get_api() == RendererAPI::API::Vulkan ? "Vulkan" : "OpenGL"); +#endif row("Workspace", tab.label); std::snprintf(buf, sizeof(buf), "%.0f x %.0f", io.DisplaySize.x * io.DisplayFramebufferScale.x, io.DisplaySize.y * io.DisplayFramebufferScale.y); diff --git a/src/ui/ui_layer.h b/src/ui/ui_layer.h index eac5273..3108c21 100644 --- a/src/ui/ui_layer.h +++ b/src/ui/ui_layer.h @@ -19,7 +19,7 @@ namespace Donut public: UILayer(); - auto init() -> void; + auto init(float dpi_scale = 1.0f) -> void; auto draw(const UIContext& ctx) -> View; private: @@ -62,6 +62,7 @@ namespace Donut int m_active_tab = 0; int m_tab_request = -1; bool m_first_frame = true; + float m_dpi_scale = 1.0f; int m_gizmo_op = 0; |
