|
|
|
@ -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_buffer;
|
|
|
|
|
VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer);
|
|
|
|
|
VkCommandBuffer* command_buffers = malloc(sizeof(VkCommandBuffer)*image_count);
|
|
|
|
|
if(command_buffers == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
for(uint32_t i = 0; i < count; i++) {
|
|
|
|
|
VkResult result = vkCreateSemaphore(device, &semaphore_info, 0, &semaphores[i]);
|
|
|
|
|
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 = {};
|
|
|
|
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
|
fence_info.flags = flags;
|
|
|
|
|
|
|
|
|
|
VkFence fence;
|
|
|
|
|
VkResult result = vkCreateFence(device, &fence_info, 0, &fence);
|
|
|
|
|
for(uint32_t i = 0; i < count; i++) {
|
|
|
|
|
VkResult result = vkCreateFence(device, &fence_info, 0, &fences[i]);
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
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->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;
|
|
|
|
|
} else {
|
|
|
|
|
context->image_available_semaphore = ia_semaphore;
|
|
|
|
|
context->swapchain_command_buffers = swapchain_command_buffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkSemaphore rf_semaphore = create_semaphore(context->device, 0);
|
|
|
|
|
if(rf_semaphore == VK_NULL_HANDLE) {
|
|
|
|
|
fprintf(stderr, "failed to create vulkan render finished semaphore\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->render_finished_semaphore = rf_semaphore;
|
|
|
|
|
context->image_available_semaphores = ia_semaphores;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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* 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->in_flight_fence = if_fence;
|
|
|
|
|
context->render_finished_semaphores = rf_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");
|
|
|
|
|
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->graphics_command_pool = command_pool;
|
|
|
|
|
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;
|
|
|
|
|