Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions include/offline_sdf_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ class OfflineSDFRenderer : public SDFRenderer {
void setupRenderContext();
void createPipeline();
void createCommandBuffers();
void destroyRenderContext();
void destroyPipeline();
void destroy();
void destroyDeviceObjects() noexcept;
void destroyRenderContextObjects() noexcept;
void destroyPipelineObjects() noexcept;
void destroy() noexcept;

void recordCommandBuffer(uint32_t slotIndex, uint32_t currentFrame);
[[nodiscard]] PPMDebugFrame debugReadbackOffscreenImage(const RingSlot &slot);
Expand All @@ -85,6 +86,7 @@ class OfflineSDFRenderer : public SDFRenderer {

void startEncoding();
void stopEncoding();
void stopEncodingNoexcept() noexcept;
void enqueueEncode(uint32_t slotIndex, uint32_t frameIndex);
void waitForSlotEncode(uint32_t slotIndex);
void runEncoderLoop();
Expand All @@ -95,6 +97,7 @@ class OfflineSDFRenderer : public SDFRenderer {
OfflineSDFRenderer(
const std::string &fragShaderPath, bool useToyTemplate = false,
OfflineRenderOptions options = {});
~OfflineSDFRenderer() noexcept;
void setup();
void renderFrames();
};
Expand Down
19 changes: 11 additions & 8 deletions include/online_sdf_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ class OnlineSDFRenderer : public SDFRenderer {
private:
// GLFW Setup
GLFWApplication app;
GLFWwindow *window;
GLFWwindow *window = nullptr;

// Vulkan Setup
VkSurfaceKHR surface;
VkSurfaceFormatKHR swapchainFormat;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkSurfaceFormatKHR swapchainFormat{};
vkutils::Semaphores imageAvailableSemaphores;
vkutils::Semaphores renderFinishedSemaphores;

// Shader Modules.
// Full screen quad vert shader + frag shader
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
VkSurfaceCapabilitiesKHR surfaceCapabilities;
VkExtent2D swapchainSize;
VkSurfaceCapabilitiesKHR surfaceCapabilities{};
VkExtent2D swapchainSize{};
vkutils::SwapchainImages swapchainImages;
vkutils::SwapchainImageViews swapchainImageViews;
vkutils::FrameBuffers frameBuffers;
Expand All @@ -61,9 +61,11 @@ class OnlineSDFRenderer : public SDFRenderer {
void createPipeline();
void tryRecreatePipeline();
void calcTimestamps(uint32_t imageIndex);
void destroyRenderContext();
void destroyPipeline();
void destroy();
void destroyRenderContextOrThrow();
void destroyDeviceObjects() noexcept;
void destroyRenderContextObjects() noexcept;
void destroyPipelineObjects() noexcept;
void destroy() noexcept;

[[nodiscard]] vkutils::PushConstants
getPushConstants(uint32_t currentFrame) noexcept;
Expand All @@ -74,6 +76,7 @@ class OnlineSDFRenderer : public SDFRenderer {
OnlineSDFRenderer(const std::string &fragShaderPath,
bool useToyTemplate = false,
OnlineRenderOptions options = {});
~OnlineSDFRenderer() noexcept;
void setup();
void gameLoop();
};
Expand Down
4 changes: 4 additions & 0 deletions include/vkutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,7 @@ destroySwapchainImageViews(VkDevice device,
vkDestroyImageView(device, swapchainImageViews.imageViews[i], nullptr);
swapchainImageViews.imageViews[i] = VK_NULL_HANDLE;
}
swapchainImageViews.count = 0;
}

static void destroyFrameBuffers(VkDevice device,
Expand All @@ -1446,13 +1447,15 @@ static void destroyFrameBuffers(VkDevice device,
vkDestroyFramebuffer(device, frameBuffers.framebuffers[i], nullptr);
frameBuffers.framebuffers[i] = VK_NULL_HANDLE;
}
frameBuffers.count = 0;
}

static void destroyFences(VkDevice device, Fences &fences) noexcept {
for (uint32_t i = 0; i < fences.count; ++i) {
vkDestroyFence(device, fences.fences[i], nullptr);
fences.fences[i] = VK_NULL_HANDLE;
}
fences.count = 0;
}

static void destroySemaphores(VkDevice device,
Expand All @@ -1461,6 +1464,7 @@ static void destroySemaphores(VkDevice device,
vkDestroySemaphore(device, semaphores.semaphores[i], nullptr);
semaphores.semaphores[i] = VK_NULL_HANDLE;
}
semaphores.count = 0;
}

} // namespace vkutils
Expand Down
104 changes: 49 additions & 55 deletions src/offline_sdf_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ OfflineSDFRenderer::OfflineSDFRenderer(
maxFrames(options.maxFrames),
encodeSettings(std::move(options.encodeSettings)) {}

OfflineSDFRenderer::~OfflineSDFRenderer() noexcept {
stopEncodingNoexcept();
destroy();
}

uint32_t OfflineSDFRenderer::validateRingSize(uint32_t value) {
if (value == 0 || value > MAX_FRAME_SLOTS) {
throw std::runtime_error("ringSize must be 1..MAX_FRAME_SLOTS");
Expand Down Expand Up @@ -358,7 +363,6 @@ void OfflineSDFRenderer::renderFrames() {
stopEncoding();

spdlog::info("Offline render done.");
destroy();
}

void OfflineSDFRenderer::startEncoding() {
Expand Down Expand Up @@ -458,6 +462,16 @@ void OfflineSDFRenderer::stopEncoding() {
encoder.reset();
}

void OfflineSDFRenderer::stopEncodingNoexcept() noexcept {
try {
stopEncoding();
} catch (const std::exception &e) {
spdlog::error("stopEncoding failed during teardown: {}", e.what());
} catch (...) {
spdlog::error("stopEncoding failed during teardown: unknown error");
}
}

void OfflineSDFRenderer::enqueueEncode(uint32_t slotIndex,
uint32_t frameIndex) {
std::unique_lock<std::mutex> lock(encodeMutex);
Expand Down Expand Up @@ -485,72 +499,52 @@ void OfflineSDFRenderer::waitForSlotEncode(uint32_t slotIndex) {
throw std::runtime_error("FFmpeg encoder failed");
}

void OfflineSDFRenderer::destroyPipeline() {
void OfflineSDFRenderer::destroyPipelineObjects() noexcept {
vkDestroyPipeline(logicalDevice, pipeline, nullptr);
pipeline = VK_NULL_HANDLE;
vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr);
pipelineLayout = VK_NULL_HANDLE;
vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr);
fragShaderModule = VK_NULL_HANDLE;
}

void OfflineSDFRenderer::destroyRenderContext() {
VK_CHECK(vkDeviceWaitIdle(logicalDevice));
void OfflineSDFRenderer::destroyRenderContextObjects() noexcept {
for (size_t i = 0; i < ringSize; ++i) {
RingSlot &slot = ringSlots[i];
if (slot.framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(logicalDevice, slot.framebuffer, nullptr);
slot.framebuffer = VK_NULL_HANDLE;
}
if (slot.imageView != VK_NULL_HANDLE) {
vkDestroyImageView(logicalDevice, slot.imageView, nullptr);
slot.imageView = VK_NULL_HANDLE;
}
if (slot.image != VK_NULL_HANDLE) {
vkDestroyImage(logicalDevice, slot.image, nullptr);
slot.image = VK_NULL_HANDLE;
}
if (slot.imageMemory != VK_NULL_HANDLE) {
vkFreeMemory(logicalDevice, slot.imageMemory, nullptr);
slot.imageMemory = VK_NULL_HANDLE;
}
if (slot.stagingBuffer.buffer != VK_NULL_HANDLE ||
slot.stagingBuffer.memory != VK_NULL_HANDLE) {
if (slot.mappedData) {
vkUnmapMemory(logicalDevice, slot.stagingBuffer.memory);
slot.mappedData = nullptr;
}
vkutils::destroyReadbackBuffer(logicalDevice, slot.stagingBuffer);
vkDestroyFramebuffer(logicalDevice, slot.framebuffer, nullptr);
vkDestroyImageView(logicalDevice, slot.imageView, nullptr);
vkDestroyImage(logicalDevice, slot.image, nullptr);
vkFreeMemory(logicalDevice, slot.imageMemory, nullptr);

if (slot.mappedData) {
vkUnmapMemory(logicalDevice, slot.stagingBuffer.memory);
}
slot.pendingReadback = false;
slot.pendingEncode = false;
vkutils::destroyReadbackBuffer(logicalDevice, slot.stagingBuffer);
}
}

void OfflineSDFRenderer::destroy() {
VK_CHECK(vkDeviceWaitIdle(logicalDevice));
vkutils::destroyFences(logicalDevice, fences);
destroyPipeline();
destroyRenderContext();
if (renderPass != VK_NULL_HANDLE) {
vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
renderPass = VK_NULL_HANDLE;
}
if (queryPool != VK_NULL_HANDLE) {
vkDestroyQueryPool(logicalDevice, queryPool, nullptr);
queryPool = VK_NULL_HANDLE;
}
if (vertShaderModule != VK_NULL_HANDLE) {
vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr);
vertShaderModule = VK_NULL_HANDLE;
}
if (commandPool != VK_NULL_HANDLE) {
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
commandPool = VK_NULL_HANDLE;
void OfflineSDFRenderer::destroyDeviceObjects() noexcept {
const VkResult waitResult = vkDeviceWaitIdle(logicalDevice);
if (waitResult != VK_SUCCESS) {
spdlog::warn(
"vkDeviceWaitIdle failed during OfflineSDFRenderer teardown: {}",
static_cast<int>(waitResult));
}
vkutils::destroyFences(logicalDevice, fences);
destroyPipelineObjects();
destroyRenderContextObjects();
vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
vkDestroyQueryPool(logicalDevice, queryPool, nullptr);
vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr);
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
}

void OfflineSDFRenderer::destroy() noexcept {
if (logicalDevice != VK_NULL_HANDLE) {
vkDestroyDevice(logicalDevice, nullptr);
logicalDevice = VK_NULL_HANDLE;
}
if (instance != VK_NULL_HANDLE) {
vkDestroyInstance(instance, nullptr);
instance = VK_NULL_HANDLE;
destroyDeviceObjects();
}
vkDestroyDevice(logicalDevice, nullptr);

// Instance owns no other offline objects at this point.
vkDestroyInstance(instance, nullptr);
}
70 changes: 46 additions & 24 deletions src/online_sdf_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ OnlineSDFRenderer::OnlineSDFRenderer(
: SDFRenderer(fragShaderPath, useToyTemplate, options.debugDumpPPMDir),
options(std::move(options)) {}

OnlineSDFRenderer::~OnlineSDFRenderer() noexcept { destroy(); }

void OnlineSDFRenderer::setup() {
glfwSetup();
vulkanSetup();
Expand Down Expand Up @@ -122,7 +124,7 @@ void OnlineSDFRenderer::tryRecreatePipeline() {
}

VK_CHECK(vkDeviceWaitIdle(logicalDevice));
destroyPipeline();
destroyPipelineObjects();
createPipelineLayoutCommon();
fragShaderModule = vkutils::createShaderModule(logicalDevice, fragSpirv);
pipeline = vkutils::createGraphicsPipeline(
Expand All @@ -135,17 +137,43 @@ void OnlineSDFRenderer::createCommandBuffers() {
swapchainImages.count);
}

void OnlineSDFRenderer::destroyPipeline() {
void OnlineSDFRenderer::destroyPipelineObjects() noexcept {
vkDestroyPipeline(logicalDevice, pipeline, nullptr);
pipeline = VK_NULL_HANDLE;
vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr);
pipelineLayout = VK_NULL_HANDLE;
vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr);
fragShaderModule = VK_NULL_HANDLE;
}

void OnlineSDFRenderer::destroyRenderContext() {
VK_CHECK(vkDeviceWaitIdle(logicalDevice));
VK_CHECK(vkResetCommandPool(logicalDevice, commandPool, 0));
void OnlineSDFRenderer::destroyRenderContextObjects() noexcept {
vkutils::destroyFrameBuffers(logicalDevice, frameBuffers);
vkutils::destroySwapchainImageViews(logicalDevice, swapchainImageViews);
}

void OnlineSDFRenderer::destroyDeviceObjects() noexcept {
const VkResult waitResult = vkDeviceWaitIdle(logicalDevice);
if (waitResult != VK_SUCCESS) {
spdlog::warn("vkDeviceWaitIdle failed during OnlineSDFRenderer teardown: {}",
static_cast<int>(waitResult));
}

vkutils::destroySemaphores(logicalDevice, imageAvailableSemaphores);
vkutils::destroySemaphores(logicalDevice, renderFinishedSemaphores);
vkutils::destroyFences(logicalDevice, fences);
destroyPipelineObjects();
vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr);
destroyRenderContextObjects();
vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
vkDestroySwapchainKHR(logicalDevice, swapchain, nullptr);
vkDestroyQueryPool(logicalDevice, queryPool, nullptr);
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
}

void OnlineSDFRenderer::destroyRenderContextOrThrow() {
VK_CHECK(vkDeviceWaitIdle(logicalDevice));
VK_CHECK(vkResetCommandPool(logicalDevice, commandPool, 0));
destroyRenderContextObjects();
// Swapchain gets destroyed after passing oldSwapchain to createSwapchain
}

Expand Down Expand Up @@ -194,7 +222,7 @@ void OnlineSDFRenderer::gameLoop() {
pipelineUpdated.store(true, std::memory_order_relaxed);
});
auto recreateSwapchain = [&]() {
destroyRenderContext();
destroyRenderContextOrThrow();
setupRenderContext();
app.framebufferResized = false;
frameIndex = 0;
Expand Down Expand Up @@ -292,27 +320,21 @@ void OnlineSDFRenderer::gameLoop() {

filewatcher->stopWatching();
spdlog::info("Done!");
destroy();
}

void OnlineSDFRenderer::destroy() {
VK_CHECK(vkDeviceWaitIdle(logicalDevice));
vkutils::destroySemaphores(logicalDevice, imageAvailableSemaphores);
vkutils::destroySemaphores(logicalDevice, renderFinishedSemaphores);
vkutils::destroyFences(logicalDevice, fences);
vkDestroyPipeline(logicalDevice, pipeline, nullptr);
vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr);
vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr);
vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr);
vkutils::destroyFrameBuffers(logicalDevice, frameBuffers);
vkDestroyRenderPass(logicalDevice, renderPass, nullptr);
vkutils::destroySwapchainImageViews(logicalDevice, swapchainImageViews);
vkDestroySwapchainKHR(logicalDevice, swapchain, nullptr);
vkDestroyQueryPool(logicalDevice, queryPool, nullptr);
vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
void OnlineSDFRenderer::destroy() noexcept {
if (logicalDevice != VK_NULL_HANDLE) {
destroyDeviceObjects();
}
vkDestroyDevice(logicalDevice, nullptr);

// The surface belongs to the instance, so destroy it first.
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();

if (window != nullptr) {
// GLFW window teardown comes last after Vulkan is gone.
glfwDestroyWindow(window);
glfwTerminate();
}
}
Loading