Merge pull request #21859 from hrydgard/vulkan-lifetime-issue

Fix a Vulkan lifetime issue causing invalid descriptors on shutdown
This commit is contained in:
Henrik Rydgård
2026-06-26 02:19:58 +02:00
committed by GitHub
6 changed files with 38 additions and 38 deletions
+1 -1
View File
@@ -1790,7 +1790,7 @@ void VulkanDeleteList::PerformDeletes(VulkanContext *vulkan, VmaAllocator alloca
int deleteCount = 0;
for (auto &callback : callbacks_) {
callback.func(vulkan, callback.userdata);
callback(vulkan);
deleteCount++;
}
callbacks_.clear();
+4 -2
View File
@@ -90,6 +90,8 @@ enum class PerfClass {
FAST,
};
typedef std::function<void(VulkanContext *)> DeleteCallback;
// This is a bit repetitive...
class VulkanDeleteList {
struct BufferWithAlloc {
@@ -127,7 +129,7 @@ public:
void QueueDeletePipelineLayout(VkPipelineLayout &pipelineLayout) { _dbg_assert_(pipelineLayout != VK_NULL_HANDLE); pipelineLayouts_.push_back(pipelineLayout); pipelineLayout = VK_NULL_HANDLE; }
void QueueDeleteDescriptorSetLayout(VkDescriptorSetLayout &descSetLayout) { _dbg_assert_(descSetLayout != VK_NULL_HANDLE); descSetLayouts_.push_back(descSetLayout); descSetLayout = VK_NULL_HANDLE; }
void QueueDeleteQueryPool(VkQueryPool &queryPool) { _dbg_assert_(queryPool != VK_NULL_HANDLE); queryPools_.push_back(queryPool); queryPool = VK_NULL_HANDLE; }
void QueueCallback(void (*func)(VulkanContext *vulkan, void *userdata), void *userdata) { callbacks_.push_back(Callback(func, userdata)); }
void QueueCallback(DeleteCallback func) { callbacks_.push_back(func); }
void QueueDeleteBufferAllocation(VkBuffer &buffer, VmaAllocation &alloc) {
_dbg_assert_(buffer != VK_NULL_HANDLE);
@@ -167,7 +169,7 @@ private:
std::vector<VkPipelineLayout> pipelineLayouts_;
std::vector<VkDescriptorSetLayout> descSetLayouts_;
std::vector<VkQueryPool> queryPools_;
std::vector<Callback> callbacks_;
std::vector<DeleteCallback> callbacks_;
int deleteCount_ = 0;
};
+4 -2
View File
@@ -975,7 +975,7 @@ void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) {
INFO_LOG(Log::G3D, "%s", StepToString(vulkan_, step).c_str());
}
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int curFrame, QueueProfileContext &profile) {
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, const int curFrame, QueueProfileContext &profile) {
for (size_t i = 0; i < step.preTransitions.size(); i++) {
const TransitionRequest &iter = step.preTransitions[i];
if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
@@ -1198,6 +1198,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::DRAW_INDEXED:
if (pipelineOK) {
_dbg_assert_(c.drawIndexed.descSetIndex < descSets->size());
VkDescriptorSet set = (*descSets)[c.drawIndexed.descSetIndex].set;
_dbg_assert_(set != VK_NULL_HANDLE);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
@@ -1210,7 +1211,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::DRAW:
if (pipelineOK) {
VkDescriptorSet set = (*descSets)[c.drawIndexed.descSetIndex].set;
_dbg_assert_(c.draw.descSetIndex < descSets->size());
VkDescriptorSet set = (*descSets)[c.draw.descSetIndex].set;
_dbg_assert_(set != VK_NULL_HANDLE);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.draw.numUboOffsets, c.draw.uboOffsets);
if (c.draw.vbuffer) {
+20 -20
View File
@@ -223,11 +223,10 @@ void VKRGraphicsPipeline::BlockUntilCompiled() {
void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
// Can't destroy variants here, the pipeline still lives for a while.
vulkan->Delete().QueueCallback([](VulkanContext *vulkan, void *p) {
VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p;
pipeline->DestroyVariantsInstant(vulkan->GetDevice());
delete pipeline;
}, this);
vulkan->Delete().QueueCallback([this](VulkanContext *vulkan) {
this->DestroyVariantsInstant(vulkan->GetDevice());
delete this;
});
}
u32 VKRGraphicsPipeline::GetVariantsBitmask() const {
@@ -528,6 +527,7 @@ VulkanRenderManager::~VulkanRenderManager() {
_dbg_assert_(!runCompileThread_); // StopThread should already have been called from DestroyBackbuffers.
vulkan_->WaitUntilQueueIdle();
vulkan_->PerformPendingDeletes(); // Some callbacks can contain a reference to the render manager.
_dbg_assert_(pipelineLayouts_.empty());
@@ -1632,11 +1632,6 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
}
frameData.Submit(vulkan_, FrameSubmitType::Pending, frameDataShared_);
// Flush descriptors.
double descStart = time_now_d();
FlushDescriptors(task.frame);
frameData.profile.descWriteTime = time_now_d() - descStart;
if (!frameData.hasMainCommands) {
// Effectively resets both main and present command buffers, since they both live in this pool.
// We always record main commands first, so we don't need to reset the present command buffer separately.
@@ -1649,6 +1644,12 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
_assert_msg_(res == VK_SUCCESS, "vkBeginCommandBuffer failed! result=%s", VulkanResultToString(res));
}
// Flush descriptors.
double descStart = time_now_d();
int f = task.frame;
FlushDescriptors(task.frame);
frameData.profile.descWriteTime = time_now_d() - descStart;
queueRunner_.PreprocessSteps(task.steps);
// Likely during shutdown, happens in headless.
if (task.steps.empty() && !frameData.hasAcquired)
@@ -1815,14 +1816,13 @@ VKRPipelineLayout *VulkanRenderManager::CreatePipelineLayout(BindingType *bindin
}
void VulkanRenderManager::DestroyPipelineLayout(VKRPipelineLayout *layout) {
for (auto iter = pipelineLayouts_.begin(); iter != pipelineLayouts_.end(); iter++) {
if (*iter == layout) {
pipelineLayouts_.erase(iter);
break;
vulkan_->Delete().QueueCallback([this, layout](VulkanContext *vulkan) {
for (auto iter = pipelineLayouts_.begin(); iter != pipelineLayouts_.end(); iter++) {
if (*iter == layout) {
pipelineLayouts_.erase(iter);
break;
}
}
}
vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *userdata) {
VKRPipelineLayout *layout = (VKRPipelineLayout *)userdata;
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
layout->frameData[i].pool.DestroyImmediately();
}
@@ -1830,17 +1830,17 @@ void VulkanRenderManager::DestroyPipelineLayout(VKRPipelineLayout *layout) {
vkDestroyDescriptorSetLayout(vulkan->GetDevice(), layout->descriptorSetLayout, nullptr);
delete layout;
}, layout);
});
}
void VulkanRenderManager::FlushDescriptors(int frame) {
for (auto iter : pipelineLayouts_) {
for (VKRPipelineLayout *iter : pipelineLayouts_) {
iter->FlushDescSets(vulkan_, frame, &frameData_[frame].profile);
}
}
void VulkanRenderManager::ResetDescriptorLists(int frame) {
for (auto iter : pipelineLayouts_) {
for (VKRPipelineLayout *iter : pipelineLayouts_) {
VKRPipelineLayout::FrameData &data = iter->frameData[frame];
data.flushedDescriptors_ = 0;
+5 -7
View File
@@ -194,10 +194,9 @@ public:
if (module_) {
VkShaderModule shaderModule = module_->BlockUntilReady();
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
vulkan_->Delete().QueueCallback([](VulkanContext *context, void *m) {
auto module = (Promise<VkShaderModule> *)m;
vulkan_->Delete().QueueCallback([module = module_](VulkanContext *context) {
delete module;
}, module_);
});
}
}
Promise<VkShaderModule> *Get() const { return module_; }
@@ -1783,10 +1782,9 @@ public:
}
~VKFramebuffer() {
_assert_msg_(buf_, "Null buf_ in VKFramebuffer - double delete?");
buf_->Vulkan()->Delete().QueueCallback([](VulkanContext *vulkan, void *fb) {
VKRFramebuffer *vfb = static_cast<VKRFramebuffer *>(fb);
delete vfb;
}, buf_);
buf_->Vulkan()->Delete().QueueCallback([buf = buf_](VulkanContext *vulkan) {
delete buf;
});
buf_ = nullptr;
}
VKRFramebuffer *GetFB() const { return buf_; }
+4 -6
View File
@@ -116,10 +116,9 @@ VulkanFragmentShader::~VulkanFragmentShader() {
if (shaderModule) {
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
}
vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *m) {
auto module = (Promise<VkShaderModule> *)m;
vulkan_->Delete().QueueCallback([module = module_](VulkanContext *vulkan) {
delete module;
}, module_);
});
}
}
@@ -148,10 +147,9 @@ VulkanVertexShader::~VulkanVertexShader() {
if (shaderModule) {
vulkan_->Delete().QueueDeleteShaderModule(shaderModule);
}
vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *m) {
auto module = (Promise<VkShaderModule> *)m;
vulkan_->Delete().QueueCallback([module = module_](VulkanContext *vulkan) {
delete module;
}, module_);
});
}
}