From 9b9c0675f0d9b2a20a18c25846a0641bea9b8b5a Mon Sep 17 00:00:00 2001 From: Amber Date: Tue, 25 Oct 2022 15:21:47 +0200 Subject: [PATCH] Add drawing functionality to actually display a triangle. --- src/main.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/src/main.c b/src/main.c index f3f8bfd..20c00c5 100644 --- a/src/main.c +++ b/src/main.c @@ -62,6 +62,15 @@ typedef struct ApplicationState { VkPipeline graphics_pipeline; VkFramebuffer* framebuffers; + + VkCommandPool commandPool; + VkCommandBuffer command_buffer; + + //synchronization objects + //semaphoers are gpu<->gpu, fences are cpu<->gpu + VkSemaphore image_available_semaph; + VkSemaphore render_finished_sempah; + VkFence in_flight_frames; } ApplicationState; typedef struct QueueFamilyIndices { @@ -598,6 +607,20 @@ void create_render_pass(ApplicationState* state) { render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass; + //subpass dependencies + VkSubpassDependency dependency = {0}; + //implicit subpass before or after rendering is complete + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + //wait on the color attachment + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + if (vkCreateRenderPass(state->device, &render_pass_info, NULL, &state->render_pass) != VK_SUCCESS) { printf("failed to create render pass"); exit(1); @@ -813,6 +836,116 @@ void create_framebuffers(ApplicationState* state) { printf("created framebuffers\n"); } +void create_command_pool(ApplicationState* state) { + QueueFamilyIndices families = find_queue_families(state, state->physical_device); + VkCommandPoolCreateInfo pool_info = {0}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + //Allow command buffers to be rerecorded individually + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_info.queueFamilyIndex = families.graphics_queue; + + if (vkCreateCommandPool(state->device, &pool_info, NULL, &state->commandPool) != VK_SUCCESS) { + printf("failed to create command pool"); + exit(1); + } + + printf("created command pools\n"); +} + +void create_command_buffer(ApplicationState* state) { + VkCommandBufferAllocateInfo command_buf_allocate_info = {0}; + command_buf_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buf_allocate_info.commandPool = state->commandPool; + //VK_COMMAND_BUFFER_LEVEL_PRIMARY: Can be submitted to a queue for execution, but cannot be called from other command buffers. + //VK_COMMAND_BUFFER_LEVEL_SECONDARY: Cannot be submitted directly, but can be called from primary command buffers + command_buf_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buf_allocate_info.commandBufferCount = 1; + + if (vkAllocateCommandBuffers(state->device, &command_buf_allocate_info, &state->command_buffer) != VK_SUCCESS) { + printf("failed to allocated command buffer"); + exit(1); + } + printf("created command buffer\n"); +} + +void record_command_buffer(ApplicationState* state, VkCommandBuffer command_buffer, uint32_t image_index) { + VkCommandBufferBeginInfo begin_info = {0}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = 0; + begin_info.pInheritanceInfo = NULL; + + if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) { + printf("failed to begin recording\n"); + exit(1); + } + + printf("start recording command buffer\n"); + + VkRenderPassBeginInfo render_pass_info = {0}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = state->render_pass; + render_pass_info.framebuffer = state->framebuffers[image_index]; + + render_pass_info.renderArea.offset.x = 0; + render_pass_info.renderArea.offset.y = 0; + render_pass_info.renderArea.extent = state->swapchain_extent; + + VkClearValue clearColor = {0}; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clearColor; + + //can also be VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS to execute command stored in secondary buffers + vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, state->graphics_pipeline); + + VkViewport viewport = {0}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)state->swapchain_extent.width; + viewport.height = (float)state->swapchain_extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + //0 and 1 are the first and the count + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + + //we want to draw all of the framebuffer + VkRect2D scissor = {0}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent = state->swapchain_extent; + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + //vertex count, instance count, first vertex and first instance + vkCmdDraw(command_buffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(command_buffer); + + if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { + printf("failed to record command buffer\n"); + exit(1); + } + + printf("recorded command buffer\n"); +} + +void create_sync_objects(ApplicationState* state) { + VkSemaphoreCreateInfo semaphore_info = {0}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fence_info = {0}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + //initialize it to signalled so we can draw the first frame + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + if (vkCreateSemaphore(state->device, &semaphore_info, NULL, &state->image_available_semaph) != VK_SUCCESS || + vkCreateSemaphore(state->device, &semaphore_info, NULL, &state->render_finished_sempah) != VK_SUCCESS || + vkCreateFence(state->device, &fence_info, NULL, &state->in_flight_frames)) { + printf("failed to create sync objects\n"); + exit(1); + } +} + void init_vulkan(ApplicationState* state) { create_instance(state); create_surface(state); @@ -823,15 +956,67 @@ void init_vulkan(ApplicationState* state) { create_render_pass(state); create_graphics_pipeline(state); create_framebuffers(state); + create_command_pool(state); + create_command_buffer(state); + create_sync_objects(state); +} + +void draw_frame(ApplicationState* state) { + vkWaitForFences(state->device, 1, &state->in_flight_frames, VK_TRUE, UINT64_MAX); + vkResetFences(state->device, 1, &state->in_flight_frames); + + uint32_t image_index; + vkAcquireNextImageKHR(state->device, state->swapchain, UINT64_MAX, state->image_available_semaph, VK_NULL_HANDLE, &image_index); + + vkResetCommandBuffer(state->command_buffer, 0); + record_command_buffer(state, state->command_buffer, image_index); + + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore to_wait[] = {state->image_available_semaph}; + VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = to_wait; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &state->command_buffer; + + VkSemaphore signal_semaphores[] = {state->render_finished_sempah}; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = signal_semaphores; + + if (vkQueueSubmit(state->graphics_queue, 1, &submit_info, state->in_flight_frames) != VK_SUCCESS) { + printf("failed to submit draw command buffer"); + exit(1); + } + + VkPresentInfoKHR present_info = {0}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = signal_semaphores; + + VkSwapchainKHR swapchains[] = {state->swapchain}; + present_info.swapchainCount = 1; + present_info.pSwapchains = swapchains; + present_info.pImageIndices = &image_index; + present_info.pResults = NULL; + + vkQueuePresentKHR(state->present_queue, &present_info); } void main_loop(ApplicationState* state) { while(!glfwWindowShouldClose(state->window)) { + draw_frame(state); glfwPollEvents(); } } void terminate(ApplicationState* state) { + vkDestroyFence(state->device, state->in_flight_frames, NULL); + vkDestroySemaphore(state->device, state->image_available_semaph, NULL); + vkDestroySemaphore(state->device, state->render_finished_sempah, NULL); + vkDestroyCommandPool(state->device, state->commandPool, NULL); for (int i = 0; i < state->swapchain_image_count; i++) { vkDestroyFramebuffer(state->device, state->framebuffers[i], NULL); }