From 66a56c33008dee5b39d5eb5a45349f8fee7a74e9 Mon Sep 17 00:00:00 2001 From: Tony Barbour Date: Wed, 21 Sep 2016 13:10:56 -0600 Subject: demos: Change cube to use FIFO present mode And throttle using fences Change-Id: I13cd622f9a401fdb44bf7b74343589b70e340b77 --- demos/cube.c | 113 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 40 deletions(-) diff --git a/demos/cube.c b/demos/cube.c index dfedfdf0..381df5ff 100644 --- a/demos/cube.c +++ b/demos/cube.c @@ -20,6 +20,7 @@ * Author: Ian Elliott * Author: Jon Ashburn * Author: Gwan-gyeong Mun +* Author: Tony Barbour */ #define _GNU_SOURCE @@ -51,6 +52,9 @@ #define APP_SHORT_NAME "cube" #define APP_LONG_NAME "The Vulkan Cube Demo Program" +// Allow a maximum of two outstanding presentation operations. +#define FRAME_LAG 2 + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #if defined(NDEBUG) && defined(__GNUC__) @@ -319,9 +323,9 @@ struct demo { VkQueue present_queue; uint32_t graphics_queue_family_index; uint32_t present_queue_family_index; - VkSemaphore image_acquired_semaphore; - VkSemaphore draw_complete_semaphore; - VkSemaphore image_ownership_semaphore; + VkSemaphore image_acquired_semaphores[FRAME_LAG]; + VkSemaphore draw_complete_semaphores[FRAME_LAG]; + VkSemaphore image_ownership_semaphores[FRAME_LAG]; VkPhysicalDeviceProperties gpu_props; VkQueueFamilyProperties *queue_props; VkPhysicalDeviceMemoryProperties memory_properties; @@ -351,6 +355,9 @@ struct demo { uint32_t swapchainImageCount; VkSwapchainKHR swapchain; SwapchainBuffers *buffers; + VkFence fences[FRAME_LAG]; + bool fencesInited[FRAME_LAG]; + int frame_index; VkCommandPool cmd_pool; VkCommandPool present_cmd_pool; @@ -745,13 +752,25 @@ void demo_update_data_buffer(struct demo *demo) { static void demo_draw(struct demo *demo) { VkResult U_ASSERT_ONLY err; + if (demo->fencesInited[demo->frame_index]) + { + // Ensure no more than FRAME_LAG presentations are outstanding + vkWaitForFences(demo->device, 1, &demo->fences[demo->frame_index], VK_TRUE, UINT64_MAX); + vkResetFences(demo->device, 1, &demo->fences[demo->frame_index]); + } + // Get the index of the next available swapchain image: err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, - demo->image_acquired_semaphore, (VkFence)0, + demo->image_acquired_semaphores[demo->frame_index], demo->fences[demo->frame_index], &demo->current_buffer); + demo->fencesInited[demo->frame_index] = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) { // demo->swapchain is out of date (e.g. the window was resized) and // must be recreated: + demo->frame_index += 1; + demo->frame_index %= FRAME_LAG; + demo_resize(demo); demo_draw(demo); return; @@ -773,11 +792,11 @@ static void demo_draw(struct demo *demo) { submit_info.pWaitDstStageMask = &pipe_stage_flags; pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &demo->image_acquired_semaphore; + submit_info.pWaitSemaphores = &demo->image_acquired_semaphores[demo->frame_index]; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &demo->buffers[demo->current_buffer].cmd; submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &demo->draw_complete_semaphore; + submit_info.pSignalSemaphores = &demo->draw_complete_semaphores[demo->frame_index]; err = vkQueueSubmit(demo->graphics_queue, 1, &submit_info, nullFence); assert(!err); @@ -787,12 +806,12 @@ static void demo_draw(struct demo *demo) { // semaphore and signalling the ownership released semaphore when finished pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &demo->draw_complete_semaphore; + submit_info.pWaitSemaphores = &demo->draw_complete_semaphores[demo->frame_index]; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &demo->buffers[demo->current_buffer].graphics_to_present_cmd; submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &demo->image_ownership_semaphore; + submit_info.pSignalSemaphores = &demo->image_ownership_semaphores[demo->frame_index]; err = vkQueueSubmit(demo->present_queue, 1, &submit_info, nullFence); assert(!err); } @@ -804,14 +823,17 @@ static void demo_draw(struct demo *demo) { .pNext = NULL, .waitSemaphoreCount = 1, .pWaitSemaphores = (demo->separate_present_queue) - ? &demo->image_ownership_semaphore - : &demo->draw_complete_semaphore, + ? &demo->image_ownership_semaphores[demo->frame_index] + : &demo->draw_complete_semaphores[demo->frame_index], .swapchainCount = 1, .pSwapchains = &demo->swapchain, .pImageIndices = &demo->current_buffer, }; err = demo->fpQueuePresentKHR(demo->present_queue, &present); + demo->frame_index += 1; + demo->frame_index %= FRAME_LAG; + if (err == VK_ERROR_OUT_OF_DATE_KHR) { // demo->swapchain is out of date (e.g. the window was resized) and // must be recreated: @@ -872,21 +894,11 @@ static void demo_prepare_buffers(struct demo *demo) { demo->height = surfCapabilities.currentExtent.height; } - // If mailbox mode is available, use it, as is the lowest-latency non- - // tearing mode. If not, try IMMEDIATE which will usually be available, - // and is fastest (though it tears). If not, fall back to FIFO which is - // always available. + // The FIFO present mode is guaranteed by the spec to be supported + // and to have no tearing. MAILBOX is another option, but for fast + // rendering apps we could use a lot of gpu power rendering frames + // that are discarded and never displayed VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - for (size_t i = 0; i < presentModeCount; i++) { - if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { - swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; - break; - } - if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && - (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { - swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - } - } // Determine the number of VkImage's to use in the swap chain. // Application desires to only acquire 1 image at a time (which is @@ -996,7 +1008,6 @@ static void demo_prepare_buffers(struct demo *demo) { VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 0); } - if (NULL != presentModes) { free(presentModes); } @@ -1948,6 +1959,19 @@ static void demo_cleanup(struct demo *demo) { demo->prepared = false; vkDeviceWaitIdle(demo->device); + // Wait for fences from present operations + for (i = 0; i < FRAME_LAG; i++) { + if (demo->fencesInited[i]) { + vkWaitForFences(demo->device, 1, &demo->fences[i], VK_TRUE, UINT64_MAX); + } + vkDestroyFence(demo->device, demo->fences[i], NULL); + vkDestroySemaphore(demo->device, demo->image_acquired_semaphores[i], NULL); + vkDestroySemaphore(demo->device, demo->draw_complete_semaphores[i], NULL); + if (demo->separate_present_queue) { + vkDestroySemaphore(demo->device, demo->image_ownership_semaphores[i], NULL); + } + } + for (i = 0; i < demo->swapchainImageCount; i++) { vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); } @@ -1981,15 +2005,11 @@ static void demo_cleanup(struct demo *demo) { &demo->buffers[i].cmd); } free(demo->buffers); - free(demo->queue_props); - vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); - vkDestroySemaphore(demo->device, demo->image_acquired_semaphore, NULL); - vkDestroySemaphore(demo->device, demo->draw_complete_semaphore, NULL); + if (demo->separate_present_queue) { vkDestroyCommandPool(demo->device, demo->present_cmd_pool, NULL); - vkDestroySemaphore(demo->device, demo->image_ownership_semaphore, NULL); } vkDestroyDevice(demo->device, NULL); if (demo->validate) { @@ -3047,19 +3067,32 @@ static void demo_init_vk_swapchain(struct demo *demo) { .pNext = NULL, .flags = 0, }; - err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, - &demo->image_acquired_semaphore); - assert(!err); - err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, - &demo->draw_complete_semaphore); - assert(!err); + // Create fences that we can use to throttle if we get too far + // ahead of the image presents + VkFenceCreateInfo fence_ci = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = NULL, + .flags = 0 + }; + for (uint32_t i = 0; i < FRAME_LAG; i++) { + vkCreateFence(demo->device, &fence_ci, NULL, &demo->fences[i]); + demo->fencesInited[i] = false; + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, + &demo->image_acquired_semaphores[i]); + assert(!err); - if (demo->separate_present_queue) { err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, - &demo->image_ownership_semaphore); + &demo->draw_complete_semaphores[i]); assert(!err); + + if (demo->separate_present_queue) { + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, + &demo->image_ownership_semaphores[i]); + assert(!err); + } } + demo->frame_index = 0; // Get Memory information and properties vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); @@ -3180,8 +3213,8 @@ static void demo_init(struct demo *demo, int argc, char **argv) { demo->width = 500; demo->height = 500; - demo->spin_angle = 0.01f; - demo->spin_increment = 0.01f; + demo->spin_angle = 4.0f; + demo->spin_increment = 0.2f; demo->pause = false; mat4x4_perspective(demo->projection_matrix, (float)degreesToRadians(45.0f), -- cgit v1.2.3