Skip to content

Commit ee8d3c4

Browse files
committed
vulkanize: Safely delete chunk buffers
There are a few kinds of vertex buffers that have been treated differently. Some are the same throughout and they are created at startup and deleted only at termination. Some are more volatile, but have constant size or have a reasonable upper bound. So those are made dynamic. Chunk buffers aren't quite like either. They stay the same for the most unless you change the landscape. Then they are completely destroyed and recreated, as is this program's wont. They have to be allowed to change, but can't be made dynamic because of the size changes and large size. So I gave up and reimplemented the GL driver approach. Whenever any buffer is deleted, it's added to a frame-specific queue. It is only deleted when that frame index is encountered again, ensuring that the command buffer also associated with that frame will have completed so any buffers that were last used in that frame can safely be destroyed.
1 parent 8f085b4 commit ee8d3c4

File tree

1 file changed

+40
-6
lines changed

1 file changed

+40
-6
lines changed

src/vkrenderer.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct BufferObj {
2323
VkBuffer buffer;
2424
VkDeviceMemory mem;
2525
VkDeviceSize size;
26+
Buffer next; // used for delete queues
2627
};
2728

2829
// Uniforms include per frame descriptor sets and ubo buffers, and the single common layout
@@ -46,6 +47,7 @@ typedef struct {
4647
VkFence fence;
4748
VkSemaphore ready;
4849
VkSemaphore done;
50+
Buffer delete_queue;
4951
} Frame;
5052

5153
// Global data regarding the vulkan renderer. All are created once and used throughout.
@@ -477,6 +479,9 @@ static int32_t create_frames(VkSwapchainKHR swapchain) {
477479
fputs("semaphore/fence creation failed\n", stderr);
478480
return VK_INCOMPLETE;// whatever
479481
}
482+
483+
// Initialize Delete queue
484+
frame->delete_queue = NULL;
480485
}
481486
free(swap_imgs);
482487

@@ -817,6 +822,7 @@ static Buffer create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemor
817822
buffer_obj->buffer = buffer;
818823
buffer_obj->mem = buf_mem;
819824
buffer_obj->size = size;
825+
buffer_obj->next = NULL;
820826

821827
return buffer_obj;
822828

@@ -849,14 +855,23 @@ static void copy_buffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
849855

850856
} // copy_buffer()
851857

852-
// Delete the buffer object represented by <buffer>
858+
// Destroy the buffer object represented by <buffer>
853859
// Destroys the vulkan buffer, frees the device memory, and frees the object
854-
void del_buffer(Buffer buf) {
855-
if (!buf) return; // it happens
860+
static void destroy_buffer(Buffer buf) {
856861
vkDestroyBuffer(vk->device, buf->buffer, NULL);
857862
vkFreeMemory(vk->device, buf->mem, NULL);
858863
free(buf);
859864

865+
} // destroy_buffer()
866+
867+
// Delete the buffer object represented by <buffer>
868+
// Actually, this queues the buffer up for deletion
869+
// the next time the current frame slot is started
870+
void del_buffer(Buffer buf) {
871+
if (!buf) return; // it happens
872+
Frame *frame = &vk->frames[vk->cur_frame];
873+
buf->next = frame->delete_queue;
874+
frame->delete_queue = buf;
860875
} // del_buffer()
861876

862877
// Generate a buffer object of <size> bytes and initialize with <data> and return its handle
@@ -1563,21 +1578,31 @@ void start_frame() {
15631578
};
15641579

15651580
vkWaitForFences(vk->device, 1, &vk->frames[vk->cur_frame].fence, VK_TRUE, ~0UL);
1581+
1582+
Frame *frame = &vk->frames[vk->cur_frame];
1583+
1584+
// Delete any vertex buffers waiting for this fence.
1585+
Buffer next = NULL;
1586+
for (Buffer buf = frame->delete_queue; buf; buf = next) {
1587+
next = buf->next;
1588+
destroy_buffer(buf);
1589+
}
1590+
frame->delete_queue = NULL;
1591+
15661592
if (vkBeginCommandBuffer(cbuf, &begin_info)) {
15671593
fputs("Command buffer Begin Failed\n",stderr);
15681594
return;
15691595
}
15701596

1571-
uint32_t cur = vk->cur_frame;
15721597
VkClearValue clear_color = {.color = {{0.0,0.0,0.0,1.0}}};
15731598
VkClearValue clear_depth = {.depthStencil = {.depth = 1.0, .stencil = 0}};
15741599
VkClearValue clears[2] = {clear_color, clear_depth};
15751600
VkRenderPassBeginInfo rp_begin_info = {
15761601
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
15771602
.renderPass = vk->render_pass,
1578-
.framebuffer = vk->frames[cur].fb,
1603+
.framebuffer = frame->fb,
15791604
.renderArea.offset = {0, 0},
1580-
.renderArea.extent = vk->frames[cur].color_buf->extent,
1605+
.renderArea.extent = frame->color_buf->extent,
15811606
.clearValueCount = 2,
15821607
.pClearValues = clears,
15831608
};
@@ -1655,11 +1680,20 @@ void shutdown_renderer() {
16551680
// Destroy all renderer resources and free any memory
16561681
// This is where every vulkan object created should be destroyed.
16571682
void del_renderer() {
1683+
16581684
del_image(vk->depth_buf);
16591685

16601686
// Destroy per-frame objects
16611687
for (int i = 0; i < vk->frame_ct; i++) {
16621688
Frame *frame = &vk->frames[i];
1689+
1690+
Buffer next = NULL;
1691+
for (Buffer buf = frame->delete_queue; buf; buf = next) {
1692+
next = buf->next;
1693+
destroy_buffer(buf);
1694+
}
1695+
frame->delete_queue = NULL;
1696+
16631697
vkDestroyFramebuffer(vk->device, frame->fb, NULL);
16641698

16651699
vkDestroyImageView(vk->device, frame->color_buf->view, NULL);

0 commit comments

Comments
 (0)