Compare commits

...
Author SHA1 Message Date
Goober5000andClaude Opus 5.5 61ad242997 fix lightning glow offset and collision LOD clamp (#8012)
Section-start glows used the b_add of the most recently created bolt
type (Nebl_type) rather than the bolt being drawn, so concurrent bolts
of different types got the wrong glow offset.  The cap glow already
used the correct type.

Clamp $Collision LOD to num_detail_levels - 1, the highest valid
detail level.  The previous bound was harmless because the collision
code falls back to the lowest existing LOD, but it was off by one.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-07 19:58:34 -05:00
dustinspace217andDustinspace217 9f2a41032d Vulkan: apply Lighting changes without a restart (#7974)
Changing Lighting in the options recompiles every shader, but Vulkan kept drawing with the pipelines it had already
built, and a pipeline keeps the shader code it was created from. So the new lighting level only showed up after a
restart. OpenGL applies it at once.

After the recompile, mark the cached pipelines stale. At the start of the next frame, before anything is recorded,
wait for the GPU and drop them; the next draws build them again from the new shaders. The recompile can run in the
middle of a frame (from the options menu or a script), so the drop waits for the next frame instead of happening there.

The recompile also rebuilds the anti-aliasing family that is not in use, which cannot compile under the current mode.
Its cached pipeline used to hide that; now the failed variant is dropped so it compiles again when that mode returns.

Fixes #7967

Co-authored-by: Dustinspace217 <[email protected]>
2026-11-07 19:56:30 +01:00
Goober5000andClaude Opus 5.5 5b6579ac0a fix lightning glow offset and collision LOD clamp
Section-start glows used the b_add of the most recently created bolt
type (Nebl_type) rather than the bolt being drawn, so concurrent bolts
of different types got the wrong glow offset.  The cap glow already
used the correct type.

Clamp $Collision LOD to num_detail_levels - 1, the highest valid
detail level.  The previous bound was harmless because the collision
code falls back to the lowest existing LOD, but it was off by one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-06 20:54:01 -05:00
6 changed files with 33 additions and 3 deletions

No files matched your search

+6
View File
@@ -121,6 +121,12 @@ public:
*/
vk::Pipeline getPipeline(const PipelineConfig& config, const vertex_layout& vertexLayout);
/**
* @brief Destroy all cached pipelines; later getPipeline calls build them again
* @note No submitted or recording command buffer may still use them
*/
void forgetAllPipelines() { m_pipelines.clear(); }
/**
* @brief Get the common pipeline layout
*
+7
View File
@@ -95,6 +95,12 @@ class VulkanRenderer {
*/
void setupFrame();
/**
* @brief Drop every cached pipeline at the start of the next frame; each is rebuilt on its next use
* Called after the shaders are recompiled, since a pipeline keeps the shader code it was built from
*/
void requestPipelineRebuild() { m_pipelinesStale = true; }
/**
* @brief End frame - ends render pass, submits, and presents
* Called at the END of each frame after all draw calls
@@ -533,6 +539,7 @@ class VulkanRenderer {
vk::CommandBuffer m_currentCommandBuffer;
SCP_vector<vk::CommandBuffer> m_currentCommandBuffers; // For cleanup
bool m_frameInProgress = false;
bool m_pipelinesStale = false;
// Swap chain recreation
bool m_swapChainNeedsRecreation = false;
@@ -101,6 +101,14 @@ void VulkanRenderer::setupFrame()
return;
}
// before recording: the recompile can run mid-frame, while the frame being recorded still holds old pipelines
if (m_pipelinesStale) {
mprintf(("Vulkan: shaders were recompiled, rebuilding pipelines\n"));
m_device->waitIdle();
m_pipelineManager->forgetAllPipelines();
m_pipelinesStale = false;
}
// Free completed texture upload command buffers
Assertion(m_textureManager, "Vulkan TextureManager not initialized in setupFrame!");
m_textureManager->frameStart();
+10 -1
View File
@@ -1,5 +1,7 @@
#include "VulkanShader.h"
#include "VulkanShaderCompiler.h"
#include "VulkanRenderer.h"
#include "gr_vulkan.h"
namespace graphics::vulkan {
@@ -29,6 +31,7 @@ void vulkan_recompile_all_shaders(const std::function<void(size_t, size_t)>& pro
{
auto* shaderManager = getShaderManager();
shaderManager->recompileAllShaders(progressCallback);
getRendererInstance()->requestPipelineRebuild();
}
VulkanShaderManager::VulkanShaderManager() = default;
@@ -143,13 +146,19 @@ void VulkanShaderManager::recompileAllShaders(const std::function<void(size_t, s
}
shader.valid = shader.vertexModule && shader.fragmentModule;
// the inactive anti-aliasing family cannot compile under the current mode, so build it again on next use
if (!shader.valid) {
m_shaderMap.erase(shader_key_t(static_cast<int>(type), flags));
m_freeSlots.push_back(static_cast<size_t>(&shader - m_shaders.data()));
}
}
}
++current;
// callers expect 0..total-1, like OpenGL; the retail options popup waits for current + 1 == total
if (progressCallback) {
progressCallback(current, total);
}
++current;
}
nprintf(("vulkan", "VulkanShaderManager: Recompiled %zu shaders\n", total));
+1 -1
View File
@@ -1116,7 +1116,7 @@ void nebl_generate_section(bolt_type *bi, float width, l_node *a, l_node *b, l_s
}
}
// calculate the glow points
nebl_calc_facing_pts_smart(&glow_a, &glow_b, &dir_normal, &a->pos, pinch_a ? 0.5f : width * 6.0f, Nebl_type->b_add);
nebl_calc_facing_pts_smart(&glow_a, &glow_b, &dir_normal, &a->pos, pinch_a ? 0.5f : width * 6.0f, bi->b_add);
g3_transfer_vertex(&c->glow_vex[0], &glow_a);
g3_transfer_vertex(&c->glow_vex[1], &glow_b);
+1 -1
View File
@@ -3041,7 +3041,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
stuff_int(&sip->collision_lod);
// Cap to sane values, just in case
sip->collision_lod = MAX(0, MIN(sip->collision_lod, sip->num_detail_levels));
sip->collision_lod = MAX(0, MIN(sip->collision_lod, sip->num_detail_levels - 1));
}
// check for optional pixel colors