refactor(cleanup): fix a memory leak & optimize reuse

This commit is contained in:
PancakeTAS 2025-12-20 20:27:13 +01:00
parent df6367cf2e
commit a354150cba
7 changed files with 62 additions and 33 deletions

View file

@ -7,6 +7,7 @@
#include "lsfg-vk-common/helpers/pointers.hpp"
#include "lsfg-vk-common/vulkan/buffer.hpp"
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
#include "lsfg-vk-common/vulkan/fence.hpp"
#include "lsfg-vk-common/vulkan/image.hpp"
#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
@ -98,7 +99,7 @@ namespace lsfgvk {
size_t fidx{0}; // real frame index
std::vector<vk::CommandBuffer> cmdbufs; // TODO: ponder reuse
size_t cmdbuf_idx{0};
vk::Fence cmdbufFence;
ls::Ctx ctx;
@ -377,7 +378,8 @@ ContextImpl::ContextImpl(const InstanceImpl& instance,
blackImage(createBlackImage(instance.getVulkan())),
syncSemaphore(importTimelineSemaphore(instance.getVulkan(), syncFd)),
prepassSemaphore(createPrepassSemaphore(instance.getVulkan())),
cmdbufs(createCommandBuffers(instance.getVulkan(), 16)),
cmdbufs(createCommandBuffers(instance.getVulkan(), destFds.size() + 1)),
cmdbufFence(instance.getVulkan()),
ctx(createCtx(instance, extent, hdr, flow, perf, destFds.size())),
mipmaps(ctx, sourceImages),
alpha0{
@ -517,8 +519,13 @@ void Instance::scheduleFrames(Context& context) {
}
void Context::scheduleFrames() {
// wait for previous pre-pass to complete
if (this->fidx && !this->cmdbufFence.wait(this->ctx.vk))
throw lsfgvk::error("Timeout waiting for previous frame to complete");
this->cmdbufFence.reset(this->ctx.vk);
// schedule pre-pass
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(this->cmdbuf_idx++ % this->cmdbufs.size());
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(0);
cmdbuf = vk::CommandBuffer(this->ctx.vk);
this->mipmaps.render(ctx.vk, cmdbuf, this->fidx);
@ -538,7 +545,7 @@ void Context::scheduleFrames() {
// schedule main passes
for (size_t i = 0; i < this->destImages.size(); i++) {
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(this->cmdbuf_idx++ % this->cmdbufs.size());
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(i + 1);
cmdbuf = vk::CommandBuffer(this->ctx.vk);
const auto& pass = this->passes.at(i);
@ -554,7 +561,8 @@ void Context::scheduleFrames() {
cmdbuf.submit(this->ctx.vk,
{}, this->prepassSemaphore.handle(), this->idx - 1,
{}, this->syncSemaphore.handle(), this->idx + i
{}, this->syncSemaphore.handle(), this->idx + i,
i == this->destImages.size() - 1 ? this->cmdbufFence.handle() : nullptr
);
}

View file

@ -101,6 +101,11 @@ namespace ls {
}
owned_ptr& operator=(owned_ptr&& other) noexcept {
if (this != &other) {
if (this->ptr) {
if (deleter) deleter(*this->ptr);
delete this->ptr;
}
ptr = other.ptr;
other.ptr = nullptr;
deleter = std::move(other.deleter);

View file

@ -73,12 +73,14 @@ namespace vk {
/// @param signalSemaphores the semaphores to signal
/// @param signalTimelineSemaphore the timeline semaphore to signal
/// @param signalValue the value to signal
/// @param fence optional fence to signal on completion
/// @throws ls::vulkan_error on failure
void submit(const vk::Vulkan& vk,
std::vector<VkSemaphore> waitSemaphores,
VkSemaphore waitTimelineSemaphore, uint64_t waitValue,
std::vector<VkSemaphore> signalSemaphores,
VkSemaphore signalTimelineSemaphore, uint64_t signalValue) const;
VkSemaphore signalTimelineSemaphore, uint64_t signalValue,
VkFence fence = nullptr) const;
/// submit the command buffer instantly
/// @param vk the vulkan instance

View file

@ -215,7 +215,8 @@ void CommandBuffer::submit(const vk::Vulkan& vk,
std::vector<VkSemaphore> waitSemaphores,
VkSemaphore waitTimelineSemaphore, uint64_t waitValue,
std::vector<VkSemaphore> signalSemaphores,
VkSemaphore signalTimelineSemaphore, uint64_t signalValue) const {
VkSemaphore signalTimelineSemaphore, uint64_t signalValue,
VkFence fence) const {
auto res = vk.df().EndCommandBuffer(*this->commandBuffer);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkEndCommandBuffer() failed");
@ -254,7 +255,7 @@ void CommandBuffer::submit(const vk::Vulkan& vk,
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
.pSignalSemaphores = signalSemaphores.data()
};
res = vk.df().QueueSubmit(vk.queue(), 1, &submitInfo, VK_NULL_HANDLE);
res = vk.df().QueueSubmit(vk.queue(), 1, &submitInfo, fence);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkQueueSubmit() failed");
}

View file

@ -10,7 +10,6 @@
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <vector>
#include <vulkan/vulkan_core.h>

View file

@ -101,17 +101,19 @@ Swapchain::Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend,
);
this->renderCommandBuffer.emplace(vk);
this->renderSemaphore.emplace(vk, 0);
this->renderFence.emplace(vk);
for (size_t i = 0; i < this->destinationImages.size(); i++) {
this->passes.emplace_back(RenderPass {
.commandBuffer = vk::CommandBuffer(vk),
.acquireSemaphore = vk::Semaphore(vk),
.postCopySemaphore = {
vk::Semaphore(vk),
vk::Semaphore(vk)
}
.acquireSemaphore = vk::Semaphore(vk)
});
}
for (size_t i = 0; i < this->info.images.size(); i++) {
this->postCopySemaphores.emplace_back(
vk::Semaphore(vk),
vk::Semaphore(vk)
);
}
}
VkResult Swapchain::present(const vk::Vulkan& vk,
@ -119,7 +121,7 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
void* next_chain, uint32_t imageIdx,
const std::vector<VkSemaphore>& semaphores) {
const auto& swapchainImage = this->info.images.at(imageIdx);
const auto& sourceImage = this->sourceImages.at(this->fidx++ % 2);
const auto& sourceImage = this->sourceImages.at(this->fidx % 2);
// schedule frame generation
this->instance.get().scheduleFrames(this->ctx.get());
@ -142,8 +144,9 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
}
// wait for completion of previous frame
auto no_timeout = this->renderSemaphore->wait(vk, this->idx - 1, 150UL * 1000 * 1000); // 150ms
if (!no_timeout) throw ls::vulkan_error(VK_TIMEOUT, "vkWaitSemaphores() failed");
if (this->fidx && !this->renderFence->wait(vk, 150UL * 1000 * 1000))
throw ls::vulkan_error(VK_TIMEOUT, "vkWaitForFences() failed");
this->renderFence->reset(vk);
// copy swapchain image into backend source image
auto& cmdbuf = this->renderCommandBuffer.emplace(vk);
@ -180,16 +183,13 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
{}, this->syncSemaphore->handle(), this->idx++
);
uint32_t lastAqImageIdx{};
for (size_t i = 0; i < this->destinationImages.size(); i++) {
auto& destinationImage = this->destinationImages.at(i);
auto& pass = this->passes.at(i);
pass = RenderPass {
.commandBuffer = vk::CommandBuffer(vk),
.acquireSemaphore = vk::Semaphore(vk),
.postCopySemaphore = {
vk::Semaphore(vk),
vk::Semaphore(vk)
}
.acquireSemaphore = vk::Semaphore(vk)
};
// acquire swapchain image
@ -204,6 +204,13 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
const auto& aquiredSwapchainImage = this->info.images.at(aqImageIdx);
// create post-copy semaphores
auto& postCopySemaphores = this->postCopySemaphores.at(aqImageIdx);
postCopySemaphores = {
vk::Semaphore(vk),
vk::Semaphore(vk)
};
// copy backend destination image into swapchain image
auto& cmdbuf = pass.commandBuffer;
@ -236,18 +243,19 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
std::vector<VkSemaphore> waitSemaphores{ pass.acquireSemaphore.handle() };
if (i) { // non-first pass
const auto& prevPass = this->passes.at(i - 1);
waitSemaphores.push_back(prevPass.postCopySemaphore.second.handle());
const auto& prevPCS = this->postCopySemaphores.at(lastAqImageIdx);
waitSemaphores.push_back(prevPCS.second.handle());
}
const std::vector<VkSemaphore> signalSemaphores{
pass.postCopySemaphore.first.handle(),
pass.postCopySemaphore.second.handle()
postCopySemaphores.first.handle(),
postCopySemaphores.second.handle()
};
cmdbuf.submit(vk,
waitSemaphores, this->syncSemaphore->handle(), this->idx,
signalSemaphores, this->renderSemaphore->handle(), this->idx
signalSemaphores, nullptr, 0,
i == this->destinationImages.size() - 1 ? this->renderFence->handle() : nullptr
);
// present swapchain image
@ -255,7 +263,7 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = i ? nullptr : next_chain,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &pass.postCopySemaphore.first.handle(),
.pWaitSemaphores = &postCopySemaphores.first.handle(),
.swapchainCount = 1,
.pSwapchains = &swapchain,
.pImageIndices = &aqImageIdx,
@ -265,15 +273,16 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
throw ls::vulkan_error(res, "vkQueuePresentKHR() failed");
lastAqImageIdx = aqImageIdx;
this->idx++;
}
// present original swapchain image
const auto& lastPass = this->passes.at(this->destinationImages.size() - 1);
auto& lastPCS = this->postCopySemaphores.at(lastAqImageIdx);
const VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &lastPass.postCopySemaphore.second.handle(),
.pWaitSemaphores = &lastPCS.second.handle(),
.swapchainCount = 1,
.pSwapchains = &swapchain,
.pImageIndices = &imageIdx,
@ -282,5 +291,8 @@ VkResult Swapchain::present(const vk::Vulkan& vk,
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
throw ls::vulkan_error(res, "vkQueuePresentKHR() failed");
this->postCopySemaphores.at(imageIdx).second = std::move(lastPCS.second);
this->fidx++;
return res;
}

View file

@ -4,6 +4,7 @@
#include "lsfg-vk-backend/lsfgvk.hpp"
#include "lsfg-vk-common/helpers/pointers.hpp"
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
#include "lsfg-vk-common/vulkan/fence.hpp"
#include "lsfg-vk-common/vulkan/image.hpp"
#include "lsfg-vk-common/vulkan/semaphore.hpp"
#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp"
@ -11,6 +12,7 @@
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>
@ -61,13 +63,13 @@ namespace lsfgvk::layer {
ls::lazy<vk::TimelineSemaphore> syncSemaphore;
std::optional<vk::CommandBuffer> renderCommandBuffer;
ls::lazy<vk::TimelineSemaphore> renderSemaphore;
ls::lazy<vk::Fence> renderFence;
struct RenderPass {
vk::CommandBuffer commandBuffer;
vk::Semaphore acquireSemaphore;
std::pair<vk::Semaphore, vk::Semaphore> postCopySemaphore;
};
std::vector<RenderPass> passes;
std::vector<std::pair<vk::Semaphore, vk::Semaphore>> postCopySemaphores;
ls::R<lsfgvk::Instance> instance;
ls::owned_ptr<ls::R<lsfgvk::Context>> ctx;