aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/application.cpp56
-rw-r--r--src/core/application.h4
-rw-r--r--src/core/log.h11
-rw-r--r--src/core/window.cpp17
-rw-r--r--src/core/window.h2
5 files changed, 72 insertions, 18 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp
index e53e420..de318fb 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> // ImGui_ImplGlfw_GetContentScaleForWindow
#include <glm/glm.hpp>
#include <algorithm>
#include <thread>
@@ -29,6 +32,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;
@@ -42,11 +46,31 @@ namespace Donut
? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL);
}
+#ifdef DONUT_NO_VULKAN
+ // generated without a Vulkan SDK, so the backend isn't in this binary at all
+ 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(); // must precede glfwInit() inside the Window ctor
+#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();
}
@@ -62,15 +86,33 @@ 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
+ // a Vulkan window can't host a GL context (GLFW_NO_API), so rebuild it
+ // and try once more on OpenGL. the saved setting stays as the user left it.
+ 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; // nothing to draw with; run() just returns
return;
}
@@ -80,9 +122,13 @@ namespace Donut
m_ui = create_scope<UILayer>();
m_device->init_imgui();
+ // windows hands out physical pixels with no scaling, so a 4k desktop at 200%
+ // would get a half-size ui. the glfw backend reports 1.0 on macos (retina
+ // scaling lives in the framebuffer there), so the mac is untouched.
+ const float dpi = ImGui_ImplGlfw_GetContentScaleForWindow((GLFWwindow*)m_window->get_native_window());
// the UI layer owns its typography and its imgui.ini section; both go into
// the device-created ImGui context before its first NewFrame reads the ini.
- m_ui->init();
+ m_ui->init(dpi);
g_prev_scroll = glfwSetScrollCallback((GLFWwindow*)m_window->get_native_window(), donut_scroll_callback);
@@ -99,8 +145,8 @@ namespace Donut
{
double frame_start = glfwGetTime();
glfwPollEvents();
- if (!m_minimized && m_device)
- render_frame();
+ if (m_minimized) { glfwWaitEventsTimeout(0.1); continue; } // nothing to draw, so don't spin a core
+ if (m_device) render_frame();
// with vsync off, cap to the target FPS (vsync itself paces otherwise).
if (!SettingsManager::get_v_sync_enabled())
@@ -179,7 +225,7 @@ namespace Donut
{
ExportConfig cfg;
cfg.width = 1280; cfg.height = 720;
- int written = export_frame(cfg);
+ [[maybe_unused]] const int written = export_frame(cfg); // only the debug log reads it
DONUT_INFO("autoexport wrote {} file(s)", written);
close();
}
diff --git a/src/core/application.h b/src/core/application.h
index bb88919..3c24ee8 100644
--- a/src/core/application.h
+++ b/src/core/application.h
@@ -69,6 +69,10 @@ namespace Donut
bool m_running = true;
bool m_minimized = false;
+ // kept for rebuilding the window when Vulkan fails and we retry on OpenGL
+ 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..ca96b26 100644
--- a/src/core/log.h
+++ b/src/core/log.h
@@ -141,16 +141,15 @@ namespace Donut
};
}
+// trace/info are debug-only chatter. warnings and errors stay in every build:
+// a release build that fails on someone else's machine should still say why.
#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 9a8d1ec..8bb4d7e 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); // before init, so a failing init says why
+
if (!s_glfw_initialized)
{
int success = glfwInit();
@@ -44,23 +46,26 @@ namespace Donut
}
else
{
-#ifdef __APPLE__
- // macOS caps OpenGL at 4.1 Core Profile and demands a forward-compat
- // core-profile context for anything modern (>= 3.3) to even compile.
- // skip these hints and GLFW hands you a dusty 2.1 context and every
- // single shader in the project refuses to build. so, we ask nicely:
+ // the shaders are GLSL 4.10 core, so ask for exactly that everywhere.
+ // macOS caps OpenGL at 4.1 and also wants forward-compat for anything
+ // past 2.1, or every shader in the project refuses to build.
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
}
+ // size the window in physical pixels for the monitor's scale (windows/x11;
+ // a no-op on macos, where the framebuffer scales instead)
+ 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;
}
@@ -70,7 +75,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);
@@ -87,6 +91,7 @@ namespace Donut
auto Window::shutdown() -> void
{
DONUT_INFO("Shutting down window: ", m_title);
+ if (!m_window) return; // creation failed, and init() already cleaned up GLFW
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;