From e3aa33fc9a99d8b817bda42a567a4c347e18ab32 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 23 Aug 2026 05:53:33 +0200 Subject: [add]: unified interface --- src/platform/vulkan/vulkan_renderer.cpp | 58 ++++++++++++++++++++----- src/platform/vulkan/vulkan_renderer.h | 77 ++++++++++++--------------------- 2 files changed, 74 insertions(+), 61 deletions(-) (limited to 'src/platform/vulkan') diff --git a/src/platform/vulkan/vulkan_renderer.cpp b/src/platform/vulkan/vulkan_renderer.cpp index 8ecf088..8b0d0c0 100644 --- a/src/platform/vulkan/vulkan_renderer.cpp +++ b/src/platform/vulkan/vulkan_renderer.cpp @@ -105,6 +105,9 @@ namespace Donut VkDeviceMemory cam_mem = VK_NULL_HANDLE, disk_mem = VK_NULL_HANDLE, obj_mem = VK_NULL_HANDLE, sim_mem = VK_NULL_HANDLE; void* cam_mapped = nullptr; void* sim_mapped = nullptr; + void* disk_mapped = nullptr; // disk UBO, refilled from bh_params each frame + BlackHoleParams bh_params; // live UI-tunable disk/camera parameters + std::string gpu_name; // physical device name for the UI Camera camera{ 60.0f, (float)GEO_HI_W / (float)GEO_HI_H, 0.1f, 100.0f }; bool left_was_down = false; double last_frame_time = 0.0; // for free-fly dt @@ -323,7 +326,8 @@ namespace Donut VkPhysicalDeviceProperties props{}; vkGetPhysicalDeviceProperties(physical, &props); vkGetPhysicalDeviceMemoryProperties(physical, &mem_props); - DONUT_INFO("Vulkan device: {}", props.deviceName); + gpu_name = props.deviceName; + DONUT_INFO("Vulkan device: {}", gpu_name); return true; } @@ -670,12 +674,8 @@ namespace Donut camera.set_elevation(1.25f); void* p = nullptr; - vkMapMemory(device, cam_mem, 0, 128, 0, &cam_mapped); // camera UBO is refilled every frame - - // disk_r1=inner (ISCO 3 r_s), disk_r2=outer, disk_num=turbulence strength, - // thickness (unused by the thin-disk model), disk_density=exposure. - float disk_data[8] = { SagA_rs * 3.0f, SagA_rs * 12.0f, 0.4f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 }; - vkMapMemory(device, disk_mem, 0, 32, 0, &p); memcpy(p, disk_data, sizeof(disk_data)); vkUnmapMemory(device, disk_mem); + vkMapMemory(device, cam_mem, 0, 128, 0, &cam_mapped); // camera UBO refilled every frame + vkMapMemory(device, disk_mem, 0, 32, 0, &disk_mapped); // disk UBO refilled from bh_params every frame std::vector obj_data(800, 0); int num_objects = 1; memcpy(obj_data.data(), &num_objects, 4); @@ -1479,7 +1479,7 @@ namespace Donut } glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0))); cam_data.pos = pos; cam_data.right = right; cam_data.up = glm::cross(right, fwd); cam_data.fwd = fwd; - cam_data.tan_half_fov = (float)tan(glm::radians(60.0f * 0.5f)); + cam_data.tan_half_fov = (float)tan(glm::radians(bh_params.fov_degrees * 0.5f)); cam_data.aspect = (float)GEO_HI_W / (float)GEO_HI_H; // 16:9, same for both targets cam_data.moving = user_moving ? 1u : 0u; if (cam_mapped) memcpy(cam_mapped, &cam_data, sizeof(cam_data)); @@ -1495,11 +1495,26 @@ namespace Donut // Sourced from settings.toml (max_steps_static), capped GPU-safe. constexpr int kStepCeil = 15000; struct SimUBO { int steps_moving; int steps_static; float early_exit; float time; } sim; - sim.steps_static = std::clamp(SettingsManager::get_max_steps_static(), 1000, kStepCeil); + sim.steps_static = std::clamp(bh_params.quality_steps, 1000, kStepCeil); sim.steps_moving = sim.steps_static; // same budget both frames; resolution is the lever sim.early_exit = SettingsManager::get_early_exit_distance(); sim.time = (float)(glfwGetTime() - start_time); if (sim_mapped) memcpy(sim_mapped, &sim, sizeof(sim)); + + // Disk UBO, refilled live from the UI-tunable parameters. Layout matches + // the shader Disk struct: r1, r2, turbulence, slab thickness, brightness, + // temperature. Radii are in Schwarzschild radii (x SagA_rs). + const float SagA_rs = 1.269e10f; + float disk_data[8] = { + std::max(bh_params.disk_inner_rs, 3.0f) * SagA_rs, + std::max(bh_params.disk_outer_rs, bh_params.disk_inner_rs + 0.5f) * SagA_rs, + std::max(bh_params.turbulence, 0.0f), + SagA_rs * 0.1f, // slab half-thickness (fixed) + std::max(bh_params.brightness, 0.0f), + std::max(bh_params.temperature, 1000.0f), + 0.0f, 0.0f, + }; + if (disk_mapped) memcpy(disk_mapped, disk_data, sizeof(disk_data)); } static double g_ScrollAccum = 0.0; @@ -1617,6 +1632,7 @@ namespace Donut if (cube_mem) vkFreeMemory(device, cube_mem, nullptr); if (cam_mapped) { vkUnmapMemory(device, cam_mem); cam_mapped = nullptr; } if (sim_mapped) { vkUnmapMemory(device, sim_mem); sim_mapped = nullptr; } + if (disk_mapped) { vkUnmapMemory(device, disk_mem); disk_mapped = nullptr; } VkBuffer ubos[4] = { cam_buf, disk_buf, obj_buf, sim_buf }; VkDeviceMemory umem[4] = { cam_mem, disk_mem, obj_mem, sim_mem }; for (int i = 0; i < 4; ++i) { if (ubos[i]) vkDestroyBuffer(device, ubos[i], nullptr); if (umem[i]) vkFreeMemory(device, umem[i], nullptr); } @@ -1791,7 +1807,7 @@ namespace Donut return true; } - auto VulkanRenderer::init_im_gui() -> bool + auto VulkanRenderer::init_ui() -> bool { Impl& v = *m_impl; if (v.device == VK_NULL_HANDLE) return false; @@ -1835,7 +1851,7 @@ namespace Donut return true; } - auto VulkanRenderer::on_resize(int width, int height) -> void + auto VulkanRenderer::resize(int width, int height) -> void { m_impl->framebuffer_resized = true; m_impl->width = width; m_impl->height = height; @@ -1905,6 +1921,26 @@ namespace Donut if (m_impl) m_impl->selected_object = index; } + auto VulkanRenderer::black_hole_params() -> BlackHoleParams& + { + return m_impl->bh_params; + } + + auto VulkanRenderer::reset_camera() -> void + { + if (!m_impl) return; + Camera& cam = m_impl->camera; + cam.set_camera_mode(CameraMode::Orbital); // also flips is_free_fly() back + cam.set_orbital_radius(4e11); + cam.set_azimuth(0.0f); + cam.set_elevation(1.25f); + } + + auto VulkanRenderer::device_name() const -> const std::string& + { + return m_impl->gpu_name; + } + auto VulkanRenderer::draw_frame(const glm::vec4& clear_color, const std::function& build_ui) -> void { Impl& v = *m_impl; diff --git a/src/platform/vulkan/vulkan_renderer.h b/src/platform/vulkan/vulkan_renderer.h index a760cb4..d583f07 100644 --- a/src/platform/vulkan/vulkan_renderer.h +++ b/src/platform/vulkan/vulkan_renderer.h @@ -1,14 +1,12 @@ #pragma once -#include -#include -#include -#include +#include "rendering/renderer_backend.h" // Live-window Vulkan backend: owns the instance, surface, device, swapchain, // render pass, framebuffers and per-frame synchronization, and drives the -// acquire -> record -> submit -> present loop. Pure-C++ header (no vulkan.h / -// glfw leak); the GLFW window is passed as an opaque handle. +// acquire -> record -> submit -> present loop. Implements the shared +// RendererBackend interface (SceneObject / BlackHoleParams live there). Pure-C++ +// header (no vulkan.h / glfw leak); the GLFW window is passed as an opaque handle. namespace Donut { // Must be called BEFORE glfwInit() when the Vulkan API is selected: points @@ -16,55 +14,34 @@ namespace Donut // macOS/Homebrew) and configures the MoltenVK ICD / layer paths. auto vulkan_prepare_glfw() -> void; - // A placeable/editable sphere in the world-builder scene view. - struct SceneObject - { - glm::vec3 position = { 0.0f, 2.0f, 0.0f }; - float radius = 2.0f; - glm::vec3 color = { 0.85f, 0.35f, 0.2f }; - }; - - class VulkanRenderer + class VulkanRenderer : public RendererBackend { public: VulkanRenderer(); - ~VulkanRenderer(); + ~VulkanRenderer() override; // glfwWindow must be a GLFW window created with GLFW_NO_API. - auto init(void* glfwWindow, int width, int height) -> bool; - auto shutdown() -> void; - - // Creates the ImGui context + Vulkan/GLFW backends. Call after init(). - auto init_im_gui() -> bool; - - // Renders + presents one frame: clears to clearColor, then (if init_im_gui - // ran) opens an ImGui frame, invokes buildUI to populate it, and draws it. - auto draw_frame(const glm::vec4& clearColor, const std::function& buildUI = {}) -> void; - - auto on_resize(int width, int height) -> void; - - // Rebuilds the starfield environment cubemap from another equirect .hdr - // at runtime (device-idle teardown + rebuild + descriptor rewrite). - // No-op if path is already the active HDRI. - auto set_hdri(const std::string& path) -> void; - - // Filesystem path of the HDRI currently backing the cubemap. - auto current_hdri() const -> const std::string&; - - // Free-fly (WASD + mouse-look) camera vs the default orbital camera. - // Enabling seeds the fly pose from the current orbital framing (no jump). - auto set_free_fly(bool enabled) -> void; - auto is_free_fly() const -> bool; - - // Switch between the black-hole geodesic view and the world-builder scene - // view (grid/sphere/skybox drawn with real Vulkan geometry pipelines). - auto set_scene_mode(bool enabled) -> void; - auto is_scene_mode() const -> bool; - - // Runtime scene objects (spheres) drawn in scene view; the UI mutates this - // list directly. set_selected_object marks one for a rim highlight (-1 = none). - auto scene_objects() -> std::vector&; - auto set_selected_object(int index) -> void; + auto init(void* glfwWindow, int width, int height) -> bool override; + auto init_ui() -> bool override; + auto shutdown() -> void override; + auto resize(int width, int height) -> void override; + + auto draw_frame(const glm::vec4& clear_color, + const std::function& build_ui) -> void override; + + auto set_scene_mode(bool enabled) -> void override; + auto is_scene_mode() const -> bool override; + auto set_free_fly(bool enabled) -> void override; // seeds the fly pose from the orbital framing + auto is_free_fly() const -> bool override; + auto reset_camera() -> void override; + + auto set_hdri(const std::string& path) -> void override; // rebuilds the cubemap (device-idle) + auto current_hdri() const -> const std::string& override; + auto scene_objects() -> std::vector& override; + auto set_selected_object(int index) -> void override; + auto black_hole_params() -> BlackHoleParams& override; + + auto device_name() const -> const std::string& override; private: struct Impl; -- cgit v1.3