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;
uint32_t swapchain_image_count;
// Per image objects
VkImage* swapchain_images;
VkImageView* swapchain_image_views;
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;
VkCommandPool graphics_command_pool;
VkCommandBuffer triangle_command_buffer;
VkPipelineLayout triangle_pipeline_layout;
VkPipeline triangle_pipeline;
VkSemaphore image_available_semaphore;
VkSemaphore render_finished_semaphore;
VkFence in_flight_fence;
uint32_t current_frame;
} VulkanContext;
const char * validation_layers[] = {
@ -885,50 +890,67 @@ VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t
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 = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.commandPool = command_pool;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandBufferCount = 1;
alloc_info.commandBufferCount = image_count;
VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
if(command_buffers == 0) {
return 0;
}
VkCommandBuffer command_buffer;
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer);
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, command_buffers);
if(result != VK_SUCCESS) {
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 = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphore_info.flags = flags;
VkSemaphore semaphore;
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphore);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphores[i]);
if(result != VK_SUCCESS) {
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 = {};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = flags;
VkFence fence;
VkResult result = vkCreateFence(device, &fence_info, 0, &fence);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
for(uint32_t i = 0; i < count; i++) {
VkResult result = vkCreateFence(device, &fence_info, 0, &fences[i]);
if(result != VK_SUCCESS) {
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));
VkInstance instance = create_instance();
@ -1034,36 +1056,46 @@ VulkanContext* init_vulkan(GLFWwindow* window) {
context->swapchain_framebuffers = framebuffers;
}
VkSemaphore ia_semaphore = create_semaphore(context->device, 0);
if(ia_semaphore == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan image available semaphore\n");
VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family);
if(command_pool == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan graphics command pool");
return 0;
} else {
context->image_available_semaphore = ia_semaphore;
context->graphics_command_pool = command_pool;
}
VkSemaphore rf_semaphore = create_semaphore(context->device, 0);
if(rf_semaphore == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan render finished semaphore\n");
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;
} else {
context->render_finished_semaphore = rf_semaphore;
context->swapchain_command_buffers = swapchain_command_buffers;
}
VkFence if_fence = create_fence(context->device, VK_FENCE_CREATE_SIGNALED_BIT);
if(if_fence == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan in flight fence\n");
VkSemaphore* ia_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
if(ia_semaphores == 0) {
fprintf(stderr, "failed to create vulkan image available semaphores\n");
return 0;
} else {
context->in_flight_fence = if_fence;
context->image_available_semaphores = ia_semaphores;
}
VkCommandPool command_pool = create_command_pool(context->device, context->queue_indices.graphics_family);
if(command_pool == VK_NULL_HANDLE) {
fprintf(stderr, "failed to create vulkan graphics command pool");
VkSemaphore* rf_semaphores = create_semaphores(context->device, 0, max_frames_in_flight);
if(rf_semaphores == 0) {
fprintf(stderr, "failed to create vulkan render finished semaphores\n");
return 0;
} else {
context->graphics_command_pool = command_pool;
context->render_finished_semaphores = rf_semaphores;
}
VkFence* if_fences = create_fences(context->device, VK_FENCE_CREATE_SIGNALED_BIT, max_frames_in_flight);
if(if_fences == 0) {
fprintf(stderr, "failed to create vulkan in flight fence\n");
return 0;
} else {
context->in_flight_fences = if_fences;
}
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;
}
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;
}
VkResult draw_frame(VulkanContext* context) {
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) {
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) {
return result;
}
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) {
return result;
}
result = vkResetCommandBuffer(context->triangle_command_buffer, 0);
result = vkResetCommandBuffer(context->swapchain_command_buffers[context->current_frame], 0);
if(result != VK_SUCCESS) {
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) {
return result;
}
@ -1125,14 +1149,14 @@ VkResult draw_frame(VulkanContext* context) {
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
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.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.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) {
return result;
}
@ -1140,7 +1164,7 @@ VkResult draw_frame(VulkanContext* context) {
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
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.pSwapchains = &context->swapchain;
present_info.pImageIndices = &image_index;
@ -1150,10 +1174,17 @@ VkResult draw_frame(VulkanContext* context) {
}
void main_loop(GLFWwindow* window, VulkanContext* context) {
context->current_frame = 0;
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
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) {
@ -1194,7 +1225,7 @@ int main() {
return 1;
}
VulkanContext* context = init_vulkan(window);
VulkanContext* context = init_vulkan(window, 2);
if (context == 0) {
fprintf(stderr, "failed to initialize vulkan context\n");
return 2;