Multiple frames in flight

main
noah metz 2024-01-07 22:55:36 -07:00
parent ef957ee059
commit 3e1cc2ce5a
1 changed files with 89 additions and 58 deletions

@ -59,21 +59,26 @@ typedef struct VulkanContextStruct {
VkExtent2D swapchain_extent; VkExtent2D swapchain_extent;
uint32_t swapchain_image_count; uint32_t swapchain_image_count;
// Per image objects
VkImage* swapchain_images; VkImage* swapchain_images;
VkImageView* swapchain_image_views; VkImageView* swapchain_image_views;
VkFramebuffer* swapchain_framebuffers; VkFramebuffer* swapchain_framebuffers;
uint32_t max_frames_in_flight;
// Per frame objects
VkCommandBuffer* swapchain_command_buffers;
VkSemaphore* image_available_semaphores;
VkSemaphore* render_finished_semaphores;
VkFence* in_flight_fences;
VkRenderPass render_pass; VkRenderPass render_pass;
VkCommandPool graphics_command_pool; VkCommandPool graphics_command_pool;
VkCommandBuffer triangle_command_buffer;
VkPipelineLayout triangle_pipeline_layout; VkPipelineLayout triangle_pipeline_layout;
VkPipeline triangle_pipeline; VkPipeline triangle_pipeline;
VkSemaphore image_available_semaphore; uint32_t current_frame;
VkSemaphore render_finished_semaphore;
VkFence in_flight_fence;
} VulkanContext; } VulkanContext;
const char * validation_layers[] = { const char * validation_layers[] = {
@ -885,50 +890,67 @@ VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t
return vkEndCommandBuffer(command_buffer); return vkEndCommandBuffer(command_buffer);
} }
VkCommandBuffer create_command_buffer(VkDevice device, VkCommandPool command_pool) { VkCommandBuffer* create_command_buffers(VkDevice device, VkCommandPool command_pool, uint32_t image_count) {
VkCommandBufferAllocateInfo alloc_info = {}; VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = command_pool; alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1; alloc_info.commandBufferCount = image_count;
VkCommandBuffer command_buffer; VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); if(command_buffers == 0) {
return 0;
}
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, command_buffers);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return VK_NULL_HANDLE; return VK_NULL_HANDLE;
} }
return command_buffer; return command_buffers;
} }
VkSemaphore create_semaphore(VkDevice device, VkSemaphoreCreateFlags flags) { VkSemaphore* create_semaphores(VkDevice device, VkSemaphoreCreateFlags flags, uint32_t count) {
VkSemaphore* semaphores = malloc(sizeof(VkSemaphore)*count);
if(semaphores == 0) {
return 0;
}
VkSemaphoreCreateInfo semaphore_info = {}; VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_info.flags = flags; semaphore_info.flags = flags;
VkSemaphore semaphore; for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphore); VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphores[i]);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return VK_NULL_HANDLE; free(semaphores);
return 0;
} }
}
return semaphore; return semaphores;
} }
VkFence create_fence(VkDevice device, VkFenceCreateFlags flags) { VkFence* create_fences(VkDevice device, VkFenceCreateFlags flags, uint32_t count) {
VkFence* fences = malloc(sizeof(VkFence)*count);
if(fences == 0) {
return 0;
}
VkFenceCreateInfo fence_info = {}; VkFenceCreateInfo fence_info = {};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = flags; fence_info.flags = flags;
VkFence fence; for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateFence(device, &fence_info, 0, &fence); VkResult result = vkCreateFence(device, &fence_info, 0, &fences[i]);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return VK_NULL_HANDLE; free(fences);
return 0;
}
} }
return fence; return fences;
} }
VulkanContext* init_vulkan(GLFWwindow* window) { VulkanContext* init_vulkan(GLFWwindow* window, uint32_t max_frames_in_flight) {
VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext)); VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext));
VkInstance instance = create_instance(); VkInstance instance = create_instance();
@ -1034,36 +1056,46 @@ VulkanContext* init_vulkan(GLFWwindow* window) {
context->swapchain_framebuffers = framebuffers; context->swapchain_framebuffers = framebuffers;
} }
VkSemaphore ia_semaphore = create_semaphore(context->device, 0); VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family);
if(ia_semaphore == VK_NULL_HANDLE) { if(command_pool == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan image available semaphore\n"); fprintf(stderr, "failed to create vulkan graphics command pool");
return 0;
} else {
context->graphics_command_pool = command_pool;
}
context->max_frames_in_flight = max_frames_in_flight;
VkCommandBuffer* swapchain_command_buffers = create_command_buffers(context->device, context->graphics_command_pool, max_frames_in_flight);
if(swapchain_command_buffers == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan swapchain command buffer\n");
return 0; return 0;
} else { } else {
context->image_available_semaphore = ia_semaphore; context->swapchain_command_buffers = swapchain_command_buffers;
} }
VkSemaphore rf_semaphore = create_semaphore(context->device, 0); VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
if(rf_semaphore == VK_NULL_HANDLE) { if(ia_semaphores == 0) {
fprintf(stderr, "failed to create vulkan render finished semaphore\n"); fprintf(stderr, "failed to create vulkan image available semaphores\n");
return 0; return 0;
} else { } else {
context->render_finished_semaphore = rf_semaphore; context->image_available_semaphores = ia_semaphores;
} }
VkFence if_fence = create_fence(context->device, VK_FENCE_CREATE_SIGNALED_BIT); VkSemaphore* rf_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
if(if_fence == VK_NULL_HANDLE) { if(rf_semaphores == 0) {
fprintf(stderr, "failed to create vulkan in flight fence\n"); fprintf(stderr, "failed to create vulkan render finished semaphores\n");
return 0; return 0;
} else { } else {
context->in_flight_fence = if_fence; context->render_finished_semaphores = rf_semaphores;
} }
VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family); VkFence* if_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, max_frames_in_flight);
if(command_pool == VK_NULL_HANDLE) { if(if_fences == 0) {
fprintf(stderr, "failed to create vulkan graphics command pool"); fprintf(stderr, "failed to create vulkan in flight fence\n");
return 0; return 0;
} else { } else {
context->graphics_command_pool = command_pool; context->in_flight_fences = if_fences;
} }
VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 0, 0, 0, 0); VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 0, 0, 0, 0);
@ -1082,41 +1114,33 @@ VulkanContext* init_vulkan(GLFWwindow* window) {
context->triangle_pipeline = triangle_pipeline; context->triangle_pipeline = triangle_pipeline;
} }
VkCommandBuffer triangle_command_buffer = create_command_buffer(context->device, context->graphics_command_pool);
if(triangle_command_buffer == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan triangle command buffer\n");
return 0;
} else {
context->triangle_command_buffer = triangle_command_buffer;
}
return context; return context;
} }
VkResult draw_frame(VulkanContext* context) { VkResult draw_frame(VulkanContext* context) {
VkResult result; VkResult result;
result = vkWaitForFences(context->device, 1, &context->in_flight_fence, VK_TRUE, UINT64_MAX); result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
result = vkResetFences(context->device, 1, &context->in_flight_fence); result = vkResetFences(context->device, 1, &context->in_flight_fences[context->current_frame]);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
uint32_t image_index; uint32_t image_index;
result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphore, VK_NULL_HANDLE, &image_index); result = vkAcquireNextImageKHR(context->device, context->swapchain, UINT64_MAX, context->image_available_semaphores[context->current_frame], VK_NULL_HANDLE, &image_index);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
result = vkResetCommandBuffer(context->triangle_command_buffer, 0); result = vkResetCommandBuffer(context->swapchain_command_buffers[context->current_frame], 0);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
result = record_command_buffer_triangle(context->triangle_command_buffer, image_index, context->render_pass, context->swapchain_framebuffers, context->swapchain_extent, context->triangle_pipeline); result = record_command_buffer_triangle(context->swapchain_command_buffers[context->current_frame], image_index, context->render_pass, context->swapchain_framebuffers, context->swapchain_extent, context->triangle_pipeline);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
@ -1125,14 +1149,14 @@ VkResult draw_frame(VulkanContext* context) {
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1; submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &context->image_available_semaphore; submit_info.pWaitSemaphores = &context->image_available_semaphores[context->current_frame];
submit_info.pWaitDstStageMask = wait_stages; submit_info.pWaitDstStageMask = wait_stages;
submit_info.commandBufferCount = 1; submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &context->triangle_command_buffer; submit_info.pCommandBuffers = &context->swapchain_command_buffers[context->current_frame];
submit_info.signalSemaphoreCount = 1; submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &context->render_finished_semaphore; submit_info.pSignalSemaphores = &context->render_finished_semaphores[context->current_frame];
result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fence); result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fences[context->current_frame]);
if(result != VK_SUCCESS) { if(result != VK_SUCCESS) {
return result; return result;
} }
@ -1140,7 +1164,7 @@ VkResult draw_frame(VulkanContext* context) {
VkPresentInfoKHR present_info = {}; VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1; present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = &context->render_finished_semaphore; present_info.pWaitSemaphores = &context->render_finished_semaphores[context->current_frame];
present_info.swapchainCount = 1; present_info.swapchainCount = 1;
present_info.pSwapchains = &context->swapchain; present_info.pSwapchains = &context->swapchain;
present_info.pImageIndices = &image_index; present_info.pImageIndices = &image_index;
@ -1150,10 +1174,17 @@ VkResult draw_frame(VulkanContext* context) {
} }
void main_loop(GLFWwindow* window, VulkanContext* context) { void main_loop(GLFWwindow* window, VulkanContext* context) {
context->current_frame = 0;
while(!glfwWindowShouldClose(window)) { while(!glfwWindowShouldClose(window)) {
glfwPollEvents(); glfwPollEvents();
draw_frame(context); draw_frame(context);
context->current_frame += 1;
if(context->current_frame >= context->max_frames_in_flight) {
context->current_frame = 0;
} }
}
vkDeviceWaitIdle(context->device);
} }
void cleanup(GLFWwindow* window, VulkanContext* context) { void cleanup(GLFWwindow* window, VulkanContext* context) {
@ -1194,7 +1225,7 @@ int main() {
return 1; return 1;
} }
VulkanContext* context = init_vulkan(window); VulkanContext* context = init_vulkan(window, 2);
if (context == 0) { if (context == 0) {
fprintf(stderr, "failed to initialize vulkan context\n"); fprintf(stderr, "failed to initialize vulkan context\n");
return 2; return 2;