|  | @@ -26,10 +26,17 @@ static VkRect2D scissor;
 | 
	
		
			
				|  |  |  static VkPipeline pipeline;
 | 
	
		
			
				|  |  |  static VkFramebuffer* framebuffers;
 | 
	
		
			
				|  |  |  static VkCommandPool commandPool;
 | 
	
		
			
				|  |  | -static VkCommandBuffer commandBuffer;
 | 
	
		
			
				|  |  | -static VkSemaphore imageAvailableSemaphore;
 | 
	
		
			
				|  |  | -static VkSemaphore renderFinishedSemaphore;
 | 
	
		
			
				|  |  | -static VkFence inFlightFence;
 | 
	
		
			
				|  |  | +static constexpr size_t MAX_FRAMES = 2;
 | 
	
		
			
				|  |  | +static size_t currentFrame = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +typedef struct {
 | 
	
		
			
				|  |  | +    VkCommandBuffer commandBuffer;
 | 
	
		
			
				|  |  | +    VkSemaphore imageAvailableSemaphore;
 | 
	
		
			
				|  |  | +    VkSemaphore renderFinishedSemaphore;
 | 
	
		
			
				|  |  | +    VkFence inFlightFence;
 | 
	
		
			
				|  |  | +} Frame;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static Frame frames[MAX_FRAMES];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int getSurfaceFormatPoints(const VkSurfaceFormatKHR* sf) {
 | 
	
		
			
				|  |  |      if(sf->format == VK_FORMAT_B8G8R8A8_UNORM &&
 | 
	
	
		
			
				|  | @@ -360,7 +367,7 @@ static bool initCommandPool() {
 | 
	
		
			
				|  |  |          .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
 | 
	
		
			
				|  |  |          .queueFamilyIndex = graphicsFamily};
 | 
	
		
			
				|  |  |      VK_ASSERT(vkCreateCommandPool(device, &info, nullptr, &commandPool));
 | 
	
		
			
				|  |  | -    return initCommandVulkanBuffer(&commandBuffer, device, commandPool);
 | 
	
		
			
				|  |  | +    return false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
 | 
	
	
		
			
				|  | @@ -385,6 +392,19 @@ static bool fillCommandBuffer(VkCommandBuffer cb, u32 index) {
 | 
	
		
			
				|  |  |      return false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static bool initFrames() {
 | 
	
		
			
				|  |  | +    for(size_t i = 0; i < MAX_FRAMES; i++) {
 | 
	
		
			
				|  |  | +        if(initCommandVulkanBuffer(
 | 
	
		
			
				|  |  | +               &frames[i].commandBuffer, device, commandPool) ||
 | 
	
		
			
				|  |  | +           initVulkanSemaphore(&frames[i].imageAvailableSemaphore, device) ||
 | 
	
		
			
				|  |  | +           initVulkanSemaphore(&frames[i].renderFinishedSemaphore, device) ||
 | 
	
		
			
				|  |  | +           initVulkanFence(&frames[i].inFlightFence, device)) {
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return false;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  bool initVulkan() {
 | 
	
		
			
				|  |  |      return initVulkanInstance() || initVulkanDebugging() ||
 | 
	
		
			
				|  |  |             initVulkanSurface(&surface, getWindow()) || initPhysicalDevice() ||
 | 
	
	
		
			
				|  | @@ -394,38 +414,36 @@ bool initVulkan() {
 | 
	
		
			
				|  |  |             initVulkanFramebuffers(
 | 
	
		
			
				|  |  |                 &framebuffers, &images, device, renderPass, swapchainSize.width,
 | 
	
		
			
				|  |  |                 swapchainSize.height) ||
 | 
	
		
			
				|  |  | -           initCommandPool() ||
 | 
	
		
			
				|  |  | -           initVulkanSemaphore(&imageAvailableSemaphore, device) ||
 | 
	
		
			
				|  |  | -           initVulkanSemaphore(&renderFinishedSemaphore, device) ||
 | 
	
		
			
				|  |  | -           initVulkanFence(&inFlightFence, device);
 | 
	
		
			
				|  |  | +           initCommandPool() || initFrames();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static bool render() {
 | 
	
		
			
				|  |  | -    VK_ASSERT(vkWaitForFences(device, 1, &inFlightFence, true, UINT64_MAX));
 | 
	
		
			
				|  |  | -    VK_ASSERT(vkResetFences(device, 1, &inFlightFence));
 | 
	
		
			
				|  |  | +    Frame* f = frames + currentFrame;
 | 
	
		
			
				|  |  | +    VK_ASSERT(vkWaitForFences(device, 1, &f->inFlightFence, true, UINT64_MAX));
 | 
	
		
			
				|  |  | +    VK_ASSERT(vkResetFences(device, 1, &f->inFlightFence));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      uint32_t imageIndex = 0;
 | 
	
		
			
				|  |  |      VK_ASSERT(vkAcquireNextImageKHR(
 | 
	
		
			
				|  |  | -        device, swapchain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE,
 | 
	
		
			
				|  |  | -        &imageIndex));
 | 
	
		
			
				|  |  | +        device, swapchain, UINT64_MAX, f->imageAvailableSemaphore,
 | 
	
		
			
				|  |  | +        VK_NULL_HANDLE, &imageIndex));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    vkResetCommandBuffer(commandBuffer, 0);
 | 
	
		
			
				|  |  | -    fillCommandBuffer(commandBuffer, imageIndex);
 | 
	
		
			
				|  |  | +    vkResetCommandBuffer(f->commandBuffer, 0);
 | 
	
		
			
				|  |  | +    fillCommandBuffer(f->commandBuffer, imageIndex);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    VkSemaphore waitSemaphores[] = {imageAvailableSemaphore};
 | 
	
		
			
				|  |  | +    VkSemaphore waitSemaphores[] = {f->imageAvailableSemaphore};
 | 
	
		
			
				|  |  |      VkPipelineStageFlags waitStages[] = {
 | 
	
		
			
				|  |  |          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
 | 
	
		
			
				|  |  | -    VkSemaphore signalSemaphores[] = {renderFinishedSemaphore};
 | 
	
		
			
				|  |  | +    VkSemaphore signalSemaphores[] = {f->renderFinishedSemaphore};
 | 
	
		
			
				|  |  |      VkSubmitInfo info = {
 | 
	
		
			
				|  |  |          .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
 | 
	
		
			
				|  |  |          .waitSemaphoreCount = 1,
 | 
	
		
			
				|  |  |          .pWaitSemaphores = waitSemaphores,
 | 
	
		
			
				|  |  |          .pWaitDstStageMask = waitStages,
 | 
	
		
			
				|  |  |          .commandBufferCount = 1,
 | 
	
		
			
				|  |  | -        .pCommandBuffers = &commandBuffer,
 | 
	
		
			
				|  |  | +        .pCommandBuffers = &f->commandBuffer,
 | 
	
		
			
				|  |  |          .signalSemaphoreCount = 1,
 | 
	
		
			
				|  |  |          .pSignalSemaphores = signalSemaphores};
 | 
	
		
			
				|  |  | -    VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &info, inFlightFence));
 | 
	
		
			
				|  |  | +    VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &info, f->inFlightFence));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      VkPresentInfoKHR presentInfo = {
 | 
	
		
			
				|  |  |          .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
 | 
	
	
		
			
				|  | @@ -436,6 +454,7 @@ static bool render() {
 | 
	
		
			
				|  |  |          .pImageIndices = &imageIndex};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      VK_ASSERT(vkQueuePresentKHR(presentQueue, &presentInfo));
 | 
	
		
			
				|  |  | +    currentFrame = (currentFrame + 1) % MAX_FRAMES;
 | 
	
		
			
				|  |  |      return false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -446,9 +465,12 @@ void renderVulkan() {
 | 
	
		
			
				|  |  |  void destroyVulkan(void) {
 | 
	
		
			
				|  |  |      if(device != VK_NULL_HANDLE) {
 | 
	
		
			
				|  |  |          vkDeviceWaitIdle(device);
 | 
	
		
			
				|  |  | -        destroyVulkanFence(inFlightFence, device);
 | 
	
		
			
				|  |  | -        destroyVulkanSemaphore(imageAvailableSemaphore, device);
 | 
	
		
			
				|  |  | -        destroyVulkanSemaphore(renderFinishedSemaphore, device);
 | 
	
		
			
				|  |  | +        for(size_t i = 0; i < MAX_FRAMES; i++) {
 | 
	
		
			
				|  |  | +            Frame* f = frames + i;
 | 
	
		
			
				|  |  | +            destroyVulkanFence(f->inFlightFence, device);
 | 
	
		
			
				|  |  | +            destroyVulkanSemaphore(f->imageAvailableSemaphore, device);
 | 
	
		
			
				|  |  | +            destroyVulkanSemaphore(f->renderFinishedSemaphore, device);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          vkDestroyCommandPool(device, commandPool, nullptr);
 | 
	
		
			
				|  |  |          destroyVulkanFramebuffers(&framebuffers, images.amount, device);
 | 
	
		
			
				|  |  |          vkDestroyPipeline(device, pipeline, nullptr);
 |