diff --git a/src/main.c b/src/main.c index c9b1a2c..4711ca9 100644 --- a/src/main.c +++ b/src/main.c @@ -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;