From e8e859384e87a70daf4c79dbda1bf2560d224114 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 22 Feb 2026 03:49:44 -0800 Subject: [PATCH] Fix minimap MSAA pipeline, defer swapchain recreation, fix player character spawn order - Add .setMultisample() to minimap display pipeline and recreatePipelines() for MSAA changes - Defer all swapchain recreation in window.cpp to beginFrame() via markSwapchainDirty() to prevent mid-frame render pass destruction crashes on resolution/fullscreen change - Move spawnPlayerCharacter() call to after loadTestTerrain() where character renderer exists --- include/rendering/minimap.hpp | 1 + include/rendering/vk_context.hpp | 1 + src/core/application.cpp | 7 ++++++ src/core/window.cpp | 6 ++--- src/rendering/minimap.cpp | 43 ++++++++++++++++++++++++++++++++ src/rendering/renderer.cpp | 2 ++ 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/include/rendering/minimap.hpp b/include/rendering/minimap.hpp index dc4a4c47..aa5097ac 100644 --- a/include/rendering/minimap.hpp +++ b/include/rendering/minimap.hpp @@ -25,6 +25,7 @@ public: bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout, int size = 200); void shutdown(); + void recreatePipelines(); void setAssetManager(pipeline::AssetManager* am) { assetManager = am; } void setMapName(const std::string& name); diff --git a/include/rendering/vk_context.hpp b/include/rendering/vk_context.hpp index 1b3e66ec..ce6f5b2a 100644 --- a/include/rendering/vk_context.hpp +++ b/include/rendering/vk_context.hpp @@ -70,6 +70,7 @@ public: const std::vector& getSwapchainFramebuffers() const { return swapchainFramebuffers; } bool isSwapchainDirty() const { return swapchainDirty; } + void markSwapchainDirty() { swapchainDirty = true; } // MSAA VkSampleCountFlagBits getMsaaSamples() const { return msaaSamples_; } diff --git a/src/core/application.cpp b/src/core/application.cpp index 9cf16748..63fa2039 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -3099,6 +3099,13 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float LOG_INFO("Online world terrain loading initiated"); } + // Character renderer is created inside loadTestTerrain(), so spawn the + // player model now that the renderer actually exists. + if (!playerCharacterSpawned) { + spawnPlayerCharacter(); + loadEquippedWeapons(); + } + showProgress("Streaming terrain tiles...", 0.35f); // Wait for surrounding terrain tiles to stream in diff --git a/src/core/window.cpp b/src/core/window.cpp index 2c91ab3c..658cd10b 100644 --- a/src/core/window.cpp +++ b/src/core/window.cpp @@ -89,7 +89,7 @@ void Window::pollEvents() { width = event.window.data1; height = event.window.data2; if (vkContext) { - vkContext->recreateSwapchain(width, height); + vkContext->markSwapchainDirty(); } LOG_DEBUG("Window resized to ", width, "x", height); } @@ -120,7 +120,7 @@ void Window::setFullscreen(bool enable) { height = windowedHeight; } if (vkContext) { - vkContext->recreateSwapchain(width, height); + vkContext->markSwapchainDirty(); } } @@ -145,7 +145,7 @@ void Window::applyResolution(int w, int h) { windowedWidth = w; windowedHeight = h; if (vkContext) { - vkContext->recreateSwapchain(width, height); + vkContext->markSwapchainDirty(); } } diff --git a/src/rendering/minimap.cpp b/src/rendering/minimap.cpp index 47c6b619..bb513ed0 100644 --- a/src/rendering/minimap.cpp +++ b/src/rendering/minimap.cpp @@ -187,6 +187,7 @@ bool Minimap::initialize(VkContext* ctx, VkDescriptorSetLayout /*perFrameLayout* .setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE) .setNoDepthTest() .setColorBlendAttachment(PipelineBuilder::blendAlpha()) + .setMultisample(vkCtx->getMsaaSamples()) .setLayout(displayPipelineLayout) .setRenderPass(vkCtx->getImGuiRenderPass()) .setDynamicStates({ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }) @@ -233,6 +234,48 @@ void Minimap::shutdown() { vkCtx = nullptr; } +void Minimap::recreatePipelines() { + if (!vkCtx || !displayPipelineLayout) return; + VkDevice device = vkCtx->getDevice(); + + if (displayPipeline) { vkDestroyPipeline(device, displayPipeline, nullptr); displayPipeline = VK_NULL_HANDLE; } + + VkShaderModule vs, fs; + if (!vs.loadFromFile(device, "assets/shaders/minimap_display.vert.spv") || + !fs.loadFromFile(device, "assets/shaders/minimap_display.frag.spv")) { + LOG_ERROR("Minimap: failed to reload display shaders for pipeline recreation"); + return; + } + + VkVertexInputBindingDescription binding{}; + binding.binding = 0; + binding.stride = 4 * sizeof(float); + binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::vector attrs(2); + attrs[0] = { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }; + attrs[1] = { 1, 0, VK_FORMAT_R32G32_SFLOAT, 2 * sizeof(float) }; + + displayPipeline = PipelineBuilder() + .setShaders(vs.stageInfo(VK_SHADER_STAGE_VERTEX_BIT), + fs.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT)) + .setVertexInput({ binding }, attrs) + .setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) + .setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE) + .setNoDepthTest() + .setColorBlendAttachment(PipelineBuilder::blendAlpha()) + .setMultisample(vkCtx->getMsaaSamples()) + .setLayout(displayPipelineLayout) + .setRenderPass(vkCtx->getImGuiRenderPass()) + .setDynamicStates({ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }) + .build(device); + + vs.destroy(); + fs.destroy(); + + LOG_INFO("Minimap: display pipeline recreated with MSAA ", static_cast(vkCtx->getMsaaSamples()), "x"); +} + void Minimap::setMapName(const std::string& name) { if (mapName != name) { mapName = name; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 341d3eb6..6f91076a 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -778,6 +778,8 @@ void Renderer::applyMsaaChange() { if (auto* lf = skySystem->getLensFlare()) lf->recreatePipelines(); } + if (minimap) minimap->recreatePipelines(); + // Selection circle + overlay use lazy init, just destroy them VkDevice device = vkCtx->getDevice(); if (selCirclePipeline) { vkDestroyPipeline(device, selCirclePipeline, nullptr); selCirclePipeline = VK_NULL_HANDLE; }