From 062a800f63394bdde8eb0e57dd790739b7366fc9 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 2 Nov 2019 17:04:07 -0500 Subject: Add fences and load vkQueueWaitIdle function --- main.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index 68b43aa..117d665 100644 --- a/main.c +++ b/main.c @@ -95,6 +95,7 @@ struct VulkanDev { PFN_vkDestroyDevice vkDestroyDevice; PFN_vkDeviceWaitIdle vkDeviceWaitIdle; PFN_vkGetDeviceQueue vkGetDeviceQueue; + PFN_vkQueueWaitIdle vkQueueWaitIdle; PFN_vkCreateCommandPool vkCreateCommandPool; PFN_vkDestroyCommandPool vkDestroyCommandPool; PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; @@ -110,7 +111,11 @@ struct VulkanDev { PFN_vkCmdClearColorImage vkCmdClearColorImage; PFN_vkCreateSemaphore vkCreateSemaphore; PFN_vkDestroySemaphore vkDestroySemaphore; + PFN_vkCreateFence vkCreateFence; + PFN_vkDestroyFence vkDestroyFence; PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; + PFN_vkWaitForFences vkWaitForFences; + PFN_vkResetFences vkResetFences; PFN_vkQueueSubmit vkQueueSubmit; PFN_vkQueuePresentKHR vkQueuePresentKHR; }; @@ -153,6 +158,9 @@ struct RenderVulkan { VkSemaphore imageAvailable; VkSemaphore renderFinished; } semaphore; + struct FenceVulkan { + VkFence *swapchain; + } fence; } sync; }; @@ -847,6 +855,13 @@ static int rvkLoadDeviceFunctions(struct RenderVulkan *vk, VkDevice device) return -1; } + static const char *const _vkQueueWaitIdle = "vkQueueWaitIdle"; + vk->dev->vkQueueWaitIdle = (PFN_vkQueueWaitIdle)vk->api->vkGetDeviceProcAddr(device, _vkQueueWaitIdle); + if (!vk->dev->vkQueueWaitIdle) { + rvkSetErrMsg(vk, strErrLd, _vkQueueWaitIdle); + return -1; + } + static const char *const _vkCreateCommandPool = "vkCreateCommandPool"; vk->dev->vkCreateCommandPool = (PFN_vkCreateCommandPool)vk->api->vkGetDeviceProcAddr(device, _vkCreateCommandPool); if (!vk->dev->vkCreateCommandPool) { @@ -945,6 +960,20 @@ static int rvkLoadDeviceFunctions(struct RenderVulkan *vk, VkDevice device) return -1; } + static const char *const _vkCreateFence = "vkCreateFence"; + vk->dev->vkCreateFence = (PFN_vkCreateFence)vk->api->vkGetDeviceProcAddr(device, _vkCreateFence); + if (!vk->dev->vkCreateFence) { + rvkSetErrMsg(vk, strErrLd, _vkCreateFence); + return -1; + } + + static const char *const _vkDestroyFence = "vkDestroyFence"; + vk->dev->vkDestroyFence = (PFN_vkDestroyFence)vk->api->vkGetDeviceProcAddr(device, _vkDestroyFence); + if (!vk->dev->vkDestroyFence) { + rvkSetErrMsg(vk, strErrLd, _vkDestroyFence); + return -1; + } + static const char *const _vkDestroySemaphore = "vkDestroySemaphore"; vk->dev->vkDestroySemaphore = (PFN_vkDestroySemaphore)vk->api->vkGetDeviceProcAddr(device, _vkDestroySemaphore); if (!vk->dev->vkDestroySemaphore) { @@ -959,6 +988,20 @@ static int rvkLoadDeviceFunctions(struct RenderVulkan *vk, VkDevice device) return -1; } + static const char *const _vkWaitForFences = "vkWaitForFences"; + vk->dev->vkWaitForFences = (PFN_vkWaitForFences)vk->api->vkGetDeviceProcAddr(device, _vkWaitForFences); + if (!vk->dev->vkWaitForFences) { + rvkSetErrMsg(vk, strErrLd, _vkWaitForFences); + return -1; + } + + static const char *const _vkResetFences = "vkResetFences"; + vk->dev->vkResetFences = (PFN_vkResetFences)vk->api->vkGetDeviceProcAddr(device, _vkResetFences); + if (!vk->dev->vkResetFences) { + rvkSetErrMsg(vk, strErrLd, _vkResetFences); + return -1; + } + static const char *const _vkQueueSubmit = "vkQueueSubmit"; vk->dev->vkQueueSubmit = (PFN_vkQueueSubmit)vk->api->vkGetDeviceProcAddr(device, _vkQueueSubmit); if (!vk->dev->vkQueueSubmit) { @@ -1058,6 +1101,12 @@ static int rvkCreateRawSwapchain(struct RenderVulkan *vk, int width, int height) /* UNORM is *NOT* recommended for color blending, but is * useful for getting simple "HTML" colors to the screen * without manually converting to a linear color space. + * + * See: + * MinutePhysics (Henry Reich). + * "Computer Color Is Broken" + * YouTube video, 4:13. March 20, 2015. + * https://www.youtube.com/watch?v=LKnqECcg6Gw */ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR @@ -1078,13 +1127,16 @@ static int rvkCreateRawSwapchain(struct RenderVulkan *vk, int width, int height) } // TODO - VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; // TODO: Clamp vk->swapchain.extent.width = width; vk->swapchain.extent.height = height; vk->swapchain.nImages = surfaceCapabilities.minImageCount; + //vk->swapchain.nImages = surfaceCapabilities.maxImageCount; + + printf("Using %d swapchain images\n", vk->swapchain.nImages); VkSwapchainCreateInfoKHR createInfo = { 0 }; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; @@ -1111,7 +1163,6 @@ static void rvkDestroyRawSwapchain(struct RenderVulkan *vk) { if (vk->swapchain.rawSwapchain) { vk->dev->vkDestroySwapchainKHR(vk->device, vk->swapchain.rawSwapchain, ALLOC_VK); - memset(&vk->swapchain.surfaceFormat, 0, sizeof(vk->swapchain.surfaceFormat)); memset(&vk->swapchain, 0, sizeof(vk->swapchain)); } } @@ -1298,11 +1349,33 @@ int rvkCreateSyncObjects(struct RenderVulkan *vk) semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; vk->dev->vkCreateSemaphore(vk->device, &semaphoreInfo, ALLOC_VK, &vk->sync.semaphore.imageAvailable); vk->dev->vkCreateSemaphore(vk->device, &semaphoreInfo, ALLOC_VK, &vk->sync.semaphore.renderFinished); + VkFenceCreateInfo fenceInfo = { 0 }; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + vk->sync.fence.swapchain = calloc(vk->swapchain.nImages, sizeof(*vk->sync.fence.swapchain)); + VkResult result; + for (uint32_t i = 0; i < vk->swapchain.nImages; ++i) { + if ((result = vk->dev->vkCreateFence(vk->device, &fenceInfo, ALLOC_VK, &vk->sync.fence.swapchain[i]))) { + rvkSetErrMsg(vk, "Could not create render finished fence: %d", result); + return -1; + } + } return 0; } void rvkDestroySyncObjects(struct RenderVulkan *vk) { + if (vk->sync.fence.swapchain) { + for (uint32_t i = 0; i < vk->swapchain.nImages; ++i) { + if (vk->sync.fence.swapchain[i]) { + vk->dev->vkDestroyFence(vk->device, + vk->sync.fence.swapchain[i], + ALLOC_VK); + } + } + free(vk->sync.fence.swapchain); + vk->sync.fence.swapchain = NULL; + } if (vk->sync.semaphore.renderFinished) { vk->dev->vkDestroySemaphore(vk->device, vk->sync.semaphore.renderFinished, ALLOC_VK); vk->sync.semaphore.renderFinished = VK_NULL_HANDLE; @@ -1401,6 +1474,8 @@ static void rvkFatal(struct RenderVulkan *vk) int running = 1; +uint32_t framesDrawn; + PuglStatus onDisplay(PuglView *view) { struct RenderVulkan *vk = puglGetHandle(view); @@ -1418,6 +1493,17 @@ PuglStatus onDisplay(PuglView *view) return PUGL_FAILURE; } + /* If running continuously, Vulkan can blast the GPU with rendering work + * faster than it can get around to presenting it, causing RAM usage to + * grow indefinitely. We use fences to limit the number of frames to the + * number of swapchain images. This will also be required later when + * flushing persistently mapped uniform buffer ranges anyway. + */ + vk->dev->vkWaitForFences(vk->device, + 1, &vk->sync.fence.swapchain[imageIndex], + VK_TRUE, UINT64_MAX); + vk->dev->vkResetFences(vk->device, 1, &vk->sync.fence.swapchain[imageIndex]); + VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT; VkSubmitInfo submitInfo = { 0 }; @@ -1430,7 +1516,10 @@ PuglStatus onDisplay(PuglView *view) submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &vk->sync.semaphore.renderFinished; - if ((result = vk->dev->vkQueueSubmit(vk->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE))) { + if ((result = vk->dev->vkQueueSubmit(vk->graphicsQueue, + 1, &submitInfo, + vk->sync.fence.swapchain[imageIndex] + ))) { rvkSetErrMsg(vk, "Could not submit to queue: %d", result); return PUGL_FAILURE; } @@ -1446,6 +1535,7 @@ PuglStatus onDisplay(PuglView *view) vk->dev->vkQueuePresentKHR(vk->graphicsQueue, &presentInfo); + //++framesDrawn; return PUGL_SUCCESS; } @@ -1500,10 +1590,13 @@ int main() printf("Opened Vulkan Device Successfully\n"); puglSetEventFunc(view, onEvent); + //PuglFpsPrinter fpsPrinter = { puglGetTime(world) }; puglShowWindow(view); while (running) { puglPollEvents(world, -1); + //puglPostRedisplay(view); puglDispatchEvents(world); + //puglPrintFps(world, &fpsPrinter, &framesDrawn); } rvkDestroy(vk); -- cgit v1.2.1