|
|
@ -62,6 +62,15 @@ typedef struct ApplicationState { |
|
|
|
VkPipeline graphics_pipeline; |
|
|
|
VkPipeline graphics_pipeline; |
|
|
|
|
|
|
|
|
|
|
|
VkFramebuffer* framebuffers; |
|
|
|
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; |
|
|
|
} ApplicationState; |
|
|
|
|
|
|
|
|
|
|
|
typedef struct QueueFamilyIndices { |
|
|
|
typedef struct QueueFamilyIndices { |
|
|
@ -598,6 +607,20 @@ void create_render_pass(ApplicationState* state) { |
|
|
|
render_pass_info.subpassCount = 1; |
|
|
|
render_pass_info.subpassCount = 1; |
|
|
|
render_pass_info.pSubpasses = &subpass; |
|
|
|
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) { |
|
|
|
if (vkCreateRenderPass(state->device, &render_pass_info, NULL, &state->render_pass) != VK_SUCCESS) { |
|
|
|
printf("failed to create render pass"); |
|
|
|
printf("failed to create render pass"); |
|
|
|
exit(1); |
|
|
|
exit(1); |
|
|
@ -813,6 +836,116 @@ void create_framebuffers(ApplicationState* state) { |
|
|
|
printf("created framebuffers\n"); |
|
|
|
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) { |
|
|
|
void init_vulkan(ApplicationState* state) { |
|
|
|
create_instance(state); |
|
|
|
create_instance(state); |
|
|
|
create_surface(state); |
|
|
|
create_surface(state); |
|
|
@ -823,15 +956,67 @@ void init_vulkan(ApplicationState* state) { |
|
|
|
create_render_pass(state); |
|
|
|
create_render_pass(state); |
|
|
|
create_graphics_pipeline(state); |
|
|
|
create_graphics_pipeline(state); |
|
|
|
create_framebuffers(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) { |
|
|
|
void main_loop(ApplicationState* state) { |
|
|
|
while(!glfwWindowShouldClose(state->window)) { |
|
|
|
while(!glfwWindowShouldClose(state->window)) { |
|
|
|
|
|
|
|
draw_frame(state); |
|
|
|
glfwPollEvents(); |
|
|
|
glfwPollEvents(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void terminate(ApplicationState* state) { |
|
|
|
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++) { |
|
|
|
for (int i = 0; i < state->swapchain_image_count; i++) { |
|
|
|
vkDestroyFramebuffer(state->device, state->framebuffers[i], NULL); |
|
|
|
vkDestroyFramebuffer(state->device, state->framebuffers[i], NULL); |
|
|
|
} |
|
|
|
} |
|
|
|