|  |  |  | @ -35,6 +35,11 @@ typedef struct SwapchainDetailsStruct { | 
		
	
		
			
				|  |  |  |  |   uint32_t present_modes_count; | 
		
	
		
			
				|  |  |  |  | } SwapchainDetails; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | typedef struct SwapchainImagesStruct { | 
		
	
		
			
				|  |  |  |  |   VkImage* images; | 
		
	
		
			
				|  |  |  |  |   uint32_t count; | 
		
	
		
			
				|  |  |  |  | } SwapchainImages; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | typedef struct VulkanContextStruct { | 
		
	
		
			
				|  |  |  |  |   VkInstance instance; | 
		
	
		
			
				|  |  |  |  |   VkDebugUtilsMessengerEXT debug_messenger; | 
		
	
	
		
			
				
					|  |  |  | @ -48,6 +53,27 @@ typedef struct VulkanContextStruct { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   SwapchainDetails swapchain_details; | 
		
	
		
			
				|  |  |  |  |   VkSwapchainKHR swapchain; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSurfaceFormatKHR swapchain_format; | 
		
	
		
			
				|  |  |  |  |   VkPresentModeKHR swapchain_present_mode; | 
		
	
		
			
				|  |  |  |  |   VkExtent2D swapchain_extent; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   uint32_t swapchain_image_count; | 
		
	
		
			
				|  |  |  |  |   VkImage* swapchain_images; | 
		
	
		
			
				|  |  |  |  |   VkImageView* swapchain_image_views; | 
		
	
		
			
				|  |  |  |  |   VkFramebuffer* swapchain_framebuffers; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  | } VulkanContext; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | const char * validation_layers[] = { | 
		
	
	
		
			
				
					|  |  |  | @ -433,13 +459,9 @@ VkExtent2D choose_swapchain_extent(SwapchainDetails swapchain_details) { | 
		
	
		
			
				|  |  |  |  |   return swapchain_details.capabilities.currentExtent; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_details, VkSurfaceKHR surface, QueueIndices indices, VkSwapchainKHR old_swapchain) { | 
		
	
		
			
				|  |  |  |  |   VkSurfaceFormatKHR format = choose_swapchain_format(swapchain_details); | 
		
	
		
			
				|  |  |  |  |   VkPresentModeKHR present_mode = choose_present_mode(swapchain_details); | 
		
	
		
			
				|  |  |  |  |   VkExtent2D extent = choose_swapchain_extent(swapchain_details); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   uint32_t image_count = swapchain_details.capabilities.minImageCount + 1; | 
		
	
		
			
				|  |  |  |  |   uint32_t max_images = swapchain_details.capabilities.maxImageCount; | 
		
	
		
			
				|  |  |  |  | VkSwapchainKHR create_swapchain(VkDevice device, VkSurfaceFormatKHR format, VkPresentModeKHR present_mode, VkExtent2D extent, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR capabilities, QueueIndices indices, VkSwapchainKHR old_swapchain) { | 
		
	
		
			
				|  |  |  |  |   uint32_t image_count = capabilities.minImageCount + 1; | 
		
	
		
			
				|  |  |  |  |   uint32_t max_images = capabilities.maxImageCount; | 
		
	
		
			
				|  |  |  |  |   if((max_images > 0) && (image_count > max_images)) { | 
		
	
		
			
				|  |  |  |  |     image_count = max_images; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					|  |  |  | @ -465,7 +487,7 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta | 
		
	
		
			
				|  |  |  |  |     swapchain_info.pQueueFamilyIndices = 0; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   swapchain_info.preTransform = swapchain_details.capabilities.currentTransform; | 
		
	
		
			
				|  |  |  |  |   swapchain_info.preTransform = capabilities.currentTransform; | 
		
	
		
			
				|  |  |  |  |   swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; | 
		
	
		
			
				|  |  |  |  |   swapchain_info.presentMode = present_mode; | 
		
	
		
			
				|  |  |  |  |   swapchain_info.clipped = VK_TRUE; | 
		
	
	
		
			
				
					|  |  |  | @ -482,6 +504,430 @@ VkSwapchainKHR create_swapchain(VkDevice device, SwapchainDetails swapchain_deta | 
		
	
		
			
				|  |  |  |  |   return swapchain; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | SwapchainImages get_swapchain_images(VkDevice device, VkSwapchainKHR swapchain) { | 
		
	
		
			
				|  |  |  |  |   SwapchainImages images; | 
		
	
		
			
				|  |  |  |  |   images.images = 0; | 
		
	
		
			
				|  |  |  |  |   images.count = 0; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkResult result; | 
		
	
		
			
				|  |  |  |  |   result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, 0); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     images.count = 0; | 
		
	
		
			
				|  |  |  |  |     return images; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   images.images = malloc(sizeof(VkImage)*images.count); | 
		
	
		
			
				|  |  |  |  |   if(images.images == 0) { | 
		
	
		
			
				|  |  |  |  |     images.count = 0; | 
		
	
		
			
				|  |  |  |  |     return images; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   result = vkGetSwapchainImagesKHR(device, swapchain, &images.count, images.images); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     images.count = 0; | 
		
	
		
			
				|  |  |  |  |     return images; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return images; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkImageView* create_image_views(VkDevice device, uint32_t image_count, VkImage* images, VkSurfaceFormatKHR format) { | 
		
	
		
			
				|  |  |  |  |   VkImageView* image_views = malloc(sizeof(VkImageView)*image_count); | 
		
	
		
			
				|  |  |  |  |   if(image_views == 0) { | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   for(uint32_t i = 0; i < image_count; i++) { | 
		
	
		
			
				|  |  |  |  |     VkImageViewCreateInfo view_info = {}; | 
		
	
		
			
				|  |  |  |  |     view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |     view_info.image = images[i]; | 
		
	
		
			
				|  |  |  |  |     view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; | 
		
	
		
			
				|  |  |  |  |     view_info.format = format.format; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; | 
		
	
		
			
				|  |  |  |  |     view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; | 
		
	
		
			
				|  |  |  |  |     view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; | 
		
	
		
			
				|  |  |  |  |     view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | 
		
	
		
			
				|  |  |  |  |     view_info.subresourceRange.baseMipLevel = 0; | 
		
	
		
			
				|  |  |  |  |     view_info.subresourceRange.levelCount = 1; | 
		
	
		
			
				|  |  |  |  |     view_info.subresourceRange.baseArrayLayer = 0; | 
		
	
		
			
				|  |  |  |  |     view_info.subresourceRange.layerCount = 1; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     VkResult result = vkCreateImageView(device, &view_info, 0, &image_views[i]); | 
		
	
		
			
				|  |  |  |  |     if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |       free(image_views); | 
		
	
		
			
				|  |  |  |  |       return 0; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return image_views; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkFramebuffer* create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkRenderPass render_pass, VkExtent2D extent) { | 
		
	
		
			
				|  |  |  |  |   VkFramebuffer* framebuffers = malloc(sizeof(VkFramebuffer)*image_count); | 
		
	
		
			
				|  |  |  |  |   if(framebuffers == 0) { | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   for(uint32_t i = 0; i < image_count; i++) { | 
		
	
		
			
				|  |  |  |  |     VkImageView attachments[] = { | 
		
	
		
			
				|  |  |  |  |       image_views[i], | 
		
	
		
			
				|  |  |  |  |     }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     VkFramebufferCreateInfo framebuffer_info = {}; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.renderPass = render_pass; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.attachmentCount = 1; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.pAttachments = attachments; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.width = extent.width; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.height = extent.height; | 
		
	
		
			
				|  |  |  |  |     framebuffer_info.layers = 1; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     VkResult result = vkCreateFramebuffer(device, &framebuffer_info, 0, &framebuffers[i]); | 
		
	
		
			
				|  |  |  |  |     if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |       free(framebuffers); | 
		
	
		
			
				|  |  |  |  |       return 0; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return framebuffers; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkShaderModule create_shader_module(VkDevice device, const char * code, uint32_t code_size) { | 
		
	
		
			
				|  |  |  |  |   VkShaderModuleCreateInfo shader_info = {}; | 
		
	
		
			
				|  |  |  |  |   shader_info.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; | 
		
	
		
			
				|  |  |  |  |   shader_info.codeSize = code_size; | 
		
	
		
			
				|  |  |  |  |   shader_info.pCode = (uint32_t*)code; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkShaderModule shader; | 
		
	
		
			
				|  |  |  |  |   VkResult result; | 
		
	
		
			
				|  |  |  |  |   result = vkCreateShaderModule(device, &shader_info, 0, &shader); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return shader; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkShaderModule load_shader_file(uint32_t buffer_size, const char* path, VkDevice device) { | 
		
	
		
			
				|  |  |  |  |   FILE* file; | 
		
	
		
			
				|  |  |  |  |   file = fopen(path, "r"); | 
		
	
		
			
				|  |  |  |  |   if(file == 0) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   char * buffer = malloc(buffer_size); | 
		
	
		
			
				|  |  |  |  |   if(buffer == 0) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   size_t read = fread(buffer, 1, buffer_size, file); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkShaderModule shader = create_shader_module(device, buffer, read); | 
		
	
		
			
				|  |  |  |  |   free(buffer); | 
		
	
		
			
				|  |  |  |  |   return shader; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkRenderPass create_render_pass(VkDevice device, VkSurfaceFormatKHR format) { | 
		
	
		
			
				|  |  |  |  |   VkAttachmentDescription color_attachment; | 
		
	
		
			
				|  |  |  |  |   color_attachment.format = format.format; | 
		
	
		
			
				|  |  |  |  |   color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; | 
		
	
		
			
				|  |  |  |  |   color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | 
		
	
		
			
				|  |  |  |  |   color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | 
		
	
		
			
				|  |  |  |  |   color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | 
		
	
		
			
				|  |  |  |  |   color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; | 
		
	
		
			
				|  |  |  |  |   color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | 
		
	
		
			
				|  |  |  |  |   color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkAttachmentReference color_attachment_ref = {}; | 
		
	
		
			
				|  |  |  |  |   color_attachment_ref.attachment = 0; | 
		
	
		
			
				|  |  |  |  |   color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSubpassDescription subpass = {}; | 
		
	
		
			
				|  |  |  |  |   subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | 
		
	
		
			
				|  |  |  |  |   subpass.colorAttachmentCount = 1; | 
		
	
		
			
				|  |  |  |  |   subpass.pColorAttachments = &color_attachment_ref; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSubpassDependency dependency = {}; | 
		
	
		
			
				|  |  |  |  |   dependency.srcSubpass = VK_SUBPASS_EXTERNAL; | 
		
	
		
			
				|  |  |  |  |   dependency.dstSubpass = 0; | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRenderPassCreateInfo render_info = {}; | 
		
	
		
			
				|  |  |  |  |   render_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   render_info.attachmentCount = 1; | 
		
	
		
			
				|  |  |  |  |   render_info.pAttachments = &color_attachment; | 
		
	
		
			
				|  |  |  |  |   render_info.subpassCount = 1; | 
		
	
		
			
				|  |  |  |  |   render_info.pSubpasses = &subpass; | 
		
	
		
			
				|  |  |  |  |   render_info.dependencyCount = 1; | 
		
	
		
			
				|  |  |  |  |   render_info.pDependencies = &dependency; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRenderPass render_pass; | 
		
	
		
			
				|  |  |  |  |   VkResult result = vkCreateRenderPass(device, &render_info, 0, &render_pass); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return render_pass; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkPipelineLayout create_pipeline_layout(VkDevice device, uint32_t set_count, VkDescriptorSetLayout* sets, uint32_t pcr_count, VkPushConstantRange* pcrs) { | 
		
	
		
			
				|  |  |  |  |   VkPipelineLayout layout; | 
		
	
		
			
				|  |  |  |  |   VkPipelineLayoutCreateInfo layout_info = {}; | 
		
	
		
			
				|  |  |  |  |   layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   layout_info.setLayoutCount = set_count; | 
		
	
		
			
				|  |  |  |  |   layout_info.pSetLayouts = sets; | 
		
	
		
			
				|  |  |  |  |   layout_info.pushConstantRangeCount = pcr_count; | 
		
	
		
			
				|  |  |  |  |   layout_info.pPushConstantRanges = pcrs; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkResult result; | 
		
	
		
			
				|  |  |  |  |   result = vkCreatePipelineLayout(device, &layout_info, 0, &layout); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return layout; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkPipeline create_graphics_pipeline(VkDevice device, VkExtent2D extent, VkPipelineLayout layout, VkRenderPass render_pass) { | 
		
	
		
			
				|  |  |  |  |   VkShaderModule vert_shader = load_shader_file(2048, "vert.spv", device); | 
		
	
		
			
				|  |  |  |  |   fprintf(stderr, "Loaded vert_shader: %p\n", vert_shader); | 
		
	
		
			
				|  |  |  |  |   VkShaderModule frag_shader = load_shader_file(2048, "frag.spv", device); | 
		
	
		
			
				|  |  |  |  |   fprintf(stderr, "Loaded frag_shader: %p\n", frag_shader); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineShaderStageCreateInfo shader_stages[2] = {}; | 
		
	
		
			
				|  |  |  |  |   shader_stages[0].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; | 
		
	
		
			
				|  |  |  |  |   shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; | 
		
	
		
			
				|  |  |  |  |   shader_stages[0].module = vert_shader; | 
		
	
		
			
				|  |  |  |  |   shader_stages[0].pName = "main"; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   shader_stages[1].sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; | 
		
	
		
			
				|  |  |  |  |   shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; | 
		
	
		
			
				|  |  |  |  |   shader_stages[1].module = frag_shader; | 
		
	
		
			
				|  |  |  |  |   shader_stages[1].pName = "main"; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkDynamicState dynamic_states[] = { | 
		
	
		
			
				|  |  |  |  |     VK_DYNAMIC_STATE_VIEWPORT, | 
		
	
		
			
				|  |  |  |  |     VK_DYNAMIC_STATE_SCISSOR, | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  |   uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineDynamicStateCreateInfo dynamic_info = {}; | 
		
	
		
			
				|  |  |  |  |   dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   dynamic_info.dynamicStateCount = dynamic_state_count; | 
		
	
		
			
				|  |  |  |  |   dynamic_info.pDynamicStates = dynamic_states; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; | 
		
	
		
			
				|  |  |  |  |   vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   vertex_input_info.vertexBindingDescriptionCount = 0; | 
		
	
		
			
				|  |  |  |  |   vertex_input_info.pVertexBindingDescriptions = 0; | 
		
	
		
			
				|  |  |  |  |   vertex_input_info.vertexAttributeDescriptionCount = 0; | 
		
	
		
			
				|  |  |  |  |   vertex_input_info.pVertexAttributeDescriptions = 0; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineInputAssemblyStateCreateInfo input_assemvly_info = {}; | 
		
	
		
			
				|  |  |  |  |   input_assemvly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   input_assemvly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | 
		
	
		
			
				|  |  |  |  |   input_assemvly_info.primitiveRestartEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkViewport viewport = {}; | 
		
	
		
			
				|  |  |  |  |   viewport.x = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.y = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.width = (float)(extent.width); | 
		
	
		
			
				|  |  |  |  |   viewport.height = (float)(extent.height); | 
		
	
		
			
				|  |  |  |  |   viewport.minDepth = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.maxDepth = 1.0f; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRect2D scissor = {}; | 
		
	
		
			
				|  |  |  |  |   VkOffset2D scissor_offset = {.x = 0, .y = 0}; | 
		
	
		
			
				|  |  |  |  |   scissor.offset = scissor_offset; | 
		
	
		
			
				|  |  |  |  |   scissor.extent = extent; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineViewportStateCreateInfo viewport_state = {}; | 
		
	
		
			
				|  |  |  |  |   viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   viewport_state.viewportCount = 1; | 
		
	
		
			
				|  |  |  |  |   viewport_state.pViewports = &viewport; | 
		
	
		
			
				|  |  |  |  |   viewport_state.scissorCount = 1; | 
		
	
		
			
				|  |  |  |  |   viewport_state.pScissors = &scissor; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineRasterizationStateCreateInfo raster_info = {}; | 
		
	
		
			
				|  |  |  |  |   raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   raster_info.depthClampEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   raster_info.rasterizerDiscardEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   raster_info.polygonMode = VK_POLYGON_MODE_FILL; | 
		
	
		
			
				|  |  |  |  |   raster_info.lineWidth = 1.0f; | 
		
	
		
			
				|  |  |  |  |   raster_info.cullMode = VK_CULL_MODE_BACK_BIT; | 
		
	
		
			
				|  |  |  |  |   raster_info.frontFace = VK_FRONT_FACE_CLOCKWISE; | 
		
	
		
			
				|  |  |  |  |   raster_info.depthBiasEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   raster_info.depthBiasConstantFactor = 0.0f; | 
		
	
		
			
				|  |  |  |  |   raster_info.depthBiasClamp = 0.0f; | 
		
	
		
			
				|  |  |  |  |   raster_info.depthBiasSlopeFactor = 0.0f; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineMultisampleStateCreateInfo multisample_info = {}; | 
		
	
		
			
				|  |  |  |  |   multisample_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   multisample_info.sampleShadingEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | 
		
	
		
			
				|  |  |  |  |   multisample_info.minSampleShading = 1.0f; | 
		
	
		
			
				|  |  |  |  |   multisample_info.pSampleMask = 0; | 
		
	
		
			
				|  |  |  |  |   multisample_info.alphaToCoverageEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   multisample_info.alphaToOneEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineColorBlendAttachmentState color_blend_attachment = {}; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.blendEnable = VK_TRUE; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; | 
		
	
		
			
				|  |  |  |  |   color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineColorBlendStateCreateInfo color_blend_info = {}; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.logicOpEnable = VK_FALSE; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.logicOp = VK_LOGIC_OP_COPY; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.attachmentCount = 1; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.pAttachments = &color_blend_attachment; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.blendConstants[0] = 0.0f; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.blendConstants[1] = 0.0f; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.blendConstants[2] = 0.0f; | 
		
	
		
			
				|  |  |  |  |   color_blend_info.blendConstants[3] = 0.0f; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkGraphicsPipelineCreateInfo pipeline_info = {}; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.stageCount = 2; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pStages = shader_stages; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pVertexInputState = &vertex_input_info; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pInputAssemblyState = &input_assemvly_info; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pViewportState = &viewport_state; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pRasterizationState = &raster_info; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pDepthStencilState = 0; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pColorBlendState = &color_blend_info; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.pDynamicState = &dynamic_info; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.layout = layout; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.renderPass = render_pass; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.subpass = 0; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   pipeline_info.basePipelineHandle = VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   pipeline_info.basePipelineIndex = -1; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipeline pipeline; | 
		
	
		
			
				|  |  |  |  |   VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_info, 0, &pipeline); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return pipeline; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkCommandPool create_command_pool(VkDevice device, uint32_t queue_family) { | 
		
	
		
			
				|  |  |  |  |   VkCommandPoolCreateInfo pool_info = {}; | 
		
	
		
			
				|  |  |  |  |   pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | 
		
	
		
			
				|  |  |  |  |   pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; | 
		
	
		
			
				|  |  |  |  |   pool_info.queueFamilyIndex = queue_family; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkCommandPool command_pool; | 
		
	
		
			
				|  |  |  |  |   VkResult result = vkCreateCommandPool(device, &pool_info, 0, &command_pool); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return command_pool; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkResult record_command_buffer_triangle(VkCommandBuffer command_buffer, uint32_t image_index, VkRenderPass render_pass, VkFramebuffer* framebuffers, VkExtent2D extent, VkPipeline graphics_pipeline) { | 
		
	
		
			
				|  |  |  |  |   VkCommandBufferBeginInfo begin_info = {}; | 
		
	
		
			
				|  |  |  |  |   begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | 
		
	
		
			
				|  |  |  |  |   begin_info.flags = 0; | 
		
	
		
			
				|  |  |  |  |   begin_info.pInheritanceInfo = 0; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkResult result = vkBeginCommandBuffer(command_buffer, &begin_info); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return result; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRenderPassBeginInfo render_pass_info = {}; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.renderPass = render_pass; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.framebuffer = framebuffers[image_index]; | 
		
	
		
			
				|  |  |  |  |   VkOffset2D render_offset = {.x = 0, .y = 0}; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.renderArea.offset = render_offset; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.renderArea.extent = extent; | 
		
	
		
			
				|  |  |  |  |   VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.clearValueCount = 1; | 
		
	
		
			
				|  |  |  |  |   render_pass_info.pClearValues = &clear_color; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); | 
		
	
		
			
				|  |  |  |  |   vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkViewport viewport = {}; | 
		
	
		
			
				|  |  |  |  |   viewport.x = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.y = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.width = (float)(extent.width); | 
		
	
		
			
				|  |  |  |  |   viewport.height = (float)(extent.height); | 
		
	
		
			
				|  |  |  |  |   viewport.minDepth = 0.0f; | 
		
	
		
			
				|  |  |  |  |   viewport.maxDepth = 1.0f; | 
		
	
		
			
				|  |  |  |  |   vkCmdSetViewport(command_buffer, 0, 1, &viewport); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRect2D scissor = {}; | 
		
	
		
			
				|  |  |  |  |   VkOffset2D scissor_offset = {.x = 0.0f, .y = 0.0f}; | 
		
	
		
			
				|  |  |  |  |   scissor.offset = scissor_offset; | 
		
	
		
			
				|  |  |  |  |   scissor.extent = extent; | 
		
	
		
			
				|  |  |  |  |   vkCmdSetScissor(command_buffer, 0, 1, &scissor); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   vkCmdDraw(command_buffer, 3, 1, 0, 0); | 
		
	
		
			
				|  |  |  |  |   vkCmdEndRenderPass(command_buffer); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return vkEndCommandBuffer(command_buffer); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkCommandBuffer create_command_buffer(VkDevice device, VkCommandPool command_pool)  { | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkCommandBuffer command_buffer; | 
		
	
		
			
				|  |  |  |  |   VkResult result = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return VK_NULL_HANDLE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return command_buffer; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkSemaphore create_semaphore(VkDevice device, VkSemaphoreCreateFlags flags) { | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return semaphore; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkFence create_fence(VkDevice device, VkFenceCreateFlags flags) { | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return fence; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VulkanContext* init_vulkan(GLFWwindow* window) { | 
		
	
		
			
				|  |  |  |  |   VulkanContext* context = (VulkanContext*)malloc(sizeof(VulkanContext)); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -543,19 +989,170 @@ VulkanContext* init_vulkan(GLFWwindow* window) { | 
		
	
		
			
				|  |  |  |  |     context->swapchain_details = maybe_details.details; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_details, context->surface, context->queue_indices, VK_NULL_HANDLE); | 
		
	
		
			
				|  |  |  |  |   context->swapchain_format = choose_swapchain_format(context->swapchain_details); | 
		
	
		
			
				|  |  |  |  |   context->swapchain_present_mode = choose_present_mode(context->swapchain_details); | 
		
	
		
			
				|  |  |  |  |   context->swapchain_extent = choose_swapchain_extent(context->swapchain_details); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSwapchainKHR swapchain = create_swapchain(context->device, context->swapchain_format, context->swapchain_present_mode, context->swapchain_extent, context->surface, context->swapchain_details.capabilities, context->queue_indices, VK_NULL_HANDLE); | 
		
	
		
			
				|  |  |  |  |   if(swapchain == VK_NULL_HANDLE) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan swapchain\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->swapchain = swapchain; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   SwapchainImages swapchain_images = get_swapchain_images(context->device, context->swapchain); | 
		
	
		
			
				|  |  |  |  |   if(swapchain_images.count == 0) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to get vulkan swapchain images\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->swapchain_image_count = swapchain_images.count; | 
		
	
		
			
				|  |  |  |  |     context->swapchain_images = swapchain_images.images; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkImageView* image_views = create_image_views(context->device, context->swapchain_image_count, context->swapchain_images, context->swapchain_format); | 
		
	
		
			
				|  |  |  |  |   if(image_views == 0) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan image views\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->swapchain_image_views = image_views; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkRenderPass render_pass = create_render_pass(context->device, context->swapchain_format); | 
		
	
		
			
				|  |  |  |  |   if(render_pass == VK_NULL_HANDLE) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan render pass\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->render_pass = render_pass; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkFramebuffer* framebuffers = create_swapchain_framebuffers(context->device, context->swapchain_image_count, context->swapchain_image_views, context->render_pass, context->swapchain_extent); | 
		
	
		
			
				|  |  |  |  |   if(framebuffers == 0) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan framebuffers\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     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"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->image_available_semaphore = ia_semaphore; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSemaphore rf_semaphore = create_semaphore(context->device, 0); | 
		
	
		
			
				|  |  |  |  |   if(rf_semaphore == VK_NULL_HANDLE) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan render finished semaphore\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->render_finished_semaphore = rf_semaphore; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   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"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->in_flight_fence = if_fence; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipelineLayout triangle_pipeline_layout = create_pipeline_layout(device, 0, 0, 0, 0); | 
		
	
		
			
				|  |  |  |  |   if(triangle_pipeline_layout == VK_NULL_HANDLE) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan pipeline layout\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     context->triangle_pipeline_layout = triangle_pipeline_layout; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkPipeline triangle_pipeline = create_graphics_pipeline(context->device, context->swapchain_extent, context->triangle_pipeline_layout, context->render_pass); | 
		
	
		
			
				|  |  |  |  |   if(triangle_pipeline == VK_NULL_HANDLE) { | 
		
	
		
			
				|  |  |  |  |     fprintf(stderr, "failed to create vulkan graphics pipeline\n"); | 
		
	
		
			
				|  |  |  |  |     return 0; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     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; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | void main_loop(GLFWwindow* window) { | 
		
	
		
			
				|  |  |  |  | VkResult draw_frame(VulkanContext* context) { | 
		
	
		
			
				|  |  |  |  |   VkResult result; | 
		
	
		
			
				|  |  |  |  |   result = vkWaitForFences(context->device, 1, &context->in_flight_fence, VK_TRUE, UINT64_MAX); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return result; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   result = vkResetFences(context->device, 1, &context->in_flight_fence); | 
		
	
		
			
				|  |  |  |  |   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); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return result; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   result = vkResetCommandBuffer(context->triangle_command_buffer, 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); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return result; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VkSubmitInfo submit_info = {}; | 
		
	
		
			
				|  |  |  |  |   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.pWaitDstStageMask = wait_stages; | 
		
	
		
			
				|  |  |  |  |   submit_info.commandBufferCount = 1; | 
		
	
		
			
				|  |  |  |  |   submit_info.pCommandBuffers = &context->triangle_command_buffer; | 
		
	
		
			
				|  |  |  |  |   submit_info.signalSemaphoreCount = 1; | 
		
	
		
			
				|  |  |  |  |   submit_info.pSignalSemaphores = &context->render_finished_semaphore; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   result = vkQueueSubmit(context->queues.graphics, 1, &submit_info, context->in_flight_fence); | 
		
	
		
			
				|  |  |  |  |   if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |     return result; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   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.swapchainCount = 1; | 
		
	
		
			
				|  |  |  |  |   present_info.pSwapchains = &context->swapchain; | 
		
	
		
			
				|  |  |  |  |   present_info.pImageIndices = &image_index; | 
		
	
		
			
				|  |  |  |  |   present_info.pResults = 0; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return vkQueuePresentKHR(context->queues.present, &present_info); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | void main_loop(GLFWwindow* window, VulkanContext* context) { | 
		
	
		
			
				|  |  |  |  |   while(!glfwWindowShouldClose(window)) { | 
		
	
		
			
				|  |  |  |  |     glfwPollEvents(); | 
		
	
		
			
				|  |  |  |  |     draw_frame(context); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -603,7 +1200,7 @@ int main() { | 
		
	
		
			
				|  |  |  |  |     return 2; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   main_loop(window); | 
		
	
		
			
				|  |  |  |  |   main_loop(window, context); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   cleanup(window, context); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | 
 |