Hello everyone,
Disclaimer: I’m relatively new to Vulkan and still in the process of learning it.
Recently, I attempted to develop a simple “Hello world triangle” Vulkan application in C with swapchain recreation. However, I encountered an issue: upon resizing the GLFW window it occasionally freezes before recreating the swapchain for a duration ranging from 5 to 20 seconds. But sometimes it just recreates the swapchain smoothly as it should, I also checked my sychronisation and I think it is fine. Also I inserted different timings to get the function execution time, by using this i found out that sometimes the function vkDeviceWaitIdle
is the cause but i wasn’t always (VkResult is 0 = VK_SUCCESS). I’m also using asan (all pointers are valid i think) with msvc and validation layers and there are no errors. I tried fixing the issue for 2 days now and I’ve been unable to pinpoint the root cause of this problem.
Any insights or suggestions would be appreciated,
ludwig
Checking VkResult at AcquireNextImage
vkWaitForFences(vcw_dev.dev, 1, &(vcw_sync->fens[cur_frame]), VK_TRUE, UINT64_MAX);
uint32_t img_index = 0;
VkResult result = vkAcquireNextImageKHR(vcw_dev.dev, vcw_swap->swap, UINT64_MAX, vcw_sync->img_avl_semps[cur_frame], VK_NULL_HANDLE, &img_index);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swap(vcw_core, vcw_pipe_group);
return 2;
} else if (result != VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) {
printf("failed to acquire next image.\n");
return 1;
}
Main render Code with Sync
if (vcw_sync->img_fens[img_index] != VK_NULL_HANDLE) {
vkWaitForFences(vcw_dev.dev, 1, &(vcw_sync->img_fens[img_index]), VK_TRUE, UINT64_MAX);
}
vcw_sync->img_fens[img_index] = vcw_sync->fens[cur_frame];
VkSubmitInfo sub_info;
sub_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
sub_info.pNext = NULL;
VkSemaphore semps_wait[1];
semps_wait[0] = vcw_sync->img_avl_semps[cur_frame];
VkPipelineStageFlags wait_stages[1];
wait_stages[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
sub_info.waitSemaphoreCount = 1;
sub_info.pWaitSemaphores = &(semps_wait[0]);
sub_info.pWaitDstStageMask = &(wait_stages[0]);
sub_info.commandBufferCount = 1;
sub_info.pCommandBuffers = &(vcw_cmd->cmd_bufs[img_index]);
VkSemaphore semps_sig[1];
semps_sig[0] = vcw_sync->rend_fin_semps[cur_frame];
sub_info.signalSemaphoreCount = 1;
sub_info.pSignalSemaphores = &(semps_sig[0]);
vkResetFences(vcw_dev.dev, 1, &(vcw_sync->fens[cur_frame]));
vkQueueSubmit(vcw_dev.q_graph, 1, &sub_info, vcw_sync->fens[cur_frame]);
//
// present
//
VkPresentInfoKHR pres_info;
pres_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
pres_info.pNext = NULL;
pres_info.waitSemaphoreCount = 1;
pres_info.pWaitSemaphores = &(semps_sig[0]);
VkSwapchainKHR swaps[1];
swaps[0] = vcw_swap->swap;
pres_info.swapchainCount = 1;
pres_info.pSwapchains = &(swaps[0]);
pres_info.pImageIndices = &img_index;
pres_info.pResults = NULL;
result = vkQueuePresentKHR(vcw_dev.q_pres, &pres_info);
Checking VkResult at Present
result = vkQueuePresentKHR(vcw_dev.q_pres, &pres_info);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
recreate_swap(vcw_core, vcw_pipe_group);
return 2;
} else if (result != VK_SUCCESS) {
printf("failed to present.\n");
}
Recreation of Swapchain
//
// unpack group arguments
//
VCW_PhysicalDevice vcw_phy_dev = *vcw_core.phy_dev;
VCW_Device vcw_dev = *vcw_core.dev;
VCW_Surface *vcw_surf = vcw_core.surf;
VCW_Swapchain *vcw_swap = vcw_core.swap;
VCW_Renderpass *vcw_rendp = vcw_pipe_group.rendp;
VCW_Sync *vcw_sync = vcw_pipe_group.sync;
VCW_CommandPool *vcw_cmd = vcw_pipe_group.cmd;
//
// recreate swapchain
//
printf("\n");
int width = 0, height = 0;
glfwGetFramebufferSize(vcw_surf->window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(vcw_surf->window, &width, &height);
glfwWaitEvents();
}
clock_t start = clock();
// sometimes freeze occurs here
vkDeviceWaitIdle(vcw_dev.dev);
clock_t end = clock();
double elapsed_time = (end - start) / (double) CLOCKS_PER_SEC;
printf("device idle: %f\n", elapsed_time);
clean_up_sync(vcw_dev, *vcw_sync);
clean_up_frame_bufs(vcw_dev, *vcw_rendp);
clean_up_cmd_pool(vcw_dev, *vcw_cmd);
clean_up_swap(vcw_dev, *vcw_swap);
update_surface_info(vcw_surf, vcw_phy_dev);
*vcw_swap = *create_swap(vcw_dev, *vcw_surf, NULL);
*vcw_cmd = create_cmd_pool(vcw_dev, *vcw_swap);
create_frame_bufs(vcw_dev, *vcw_swap, vcw_rendp, vcw_swap->extent);
*vcw_sync = create_sync(vcw_dev, *vcw_swap, vcw_sync->max_frames);
prepare_rendering(vcw_core, vcw_pipe_group);
clean_up_frame_bufs
for (uint32_t i = 0; i < vcw_rendp.frame_buf_count; i++) {
vkDestroyFramebuffer(vcw_dev.dev, vcw_rendp.frame_bufs[i], NULL);
printf("framebuffer %d destroyed.\n", i);
}
free(vcw_rendp.frame_bufs);
create_frame_bufs
//
// create framebuffer
//
VkFramebufferCreateInfo *frame_buf_infos = malloc(vcw_swap.img_count * sizeof(VkFramebufferCreateInfo));
vcw_rendp->frame_bufs = malloc(vcw_swap.img_count * sizeof(VkFramebuffer));
VkImageView *img_attachs = malloc(vcw_swap.img_count * sizeof(VkImageView));
for (uint32_t i = 0; i < vcw_swap.img_count; i++) {
img_attachs[i] = vcw_swap.img_views[i];
frame_buf_infos[i].sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frame_buf_infos[i].pNext = NULL;
frame_buf_infos[i].flags = 0;
frame_buf_infos[i].renderPass = vcw_rendp->rendp;
frame_buf_infos[i].attachmentCount = 1;
frame_buf_infos[i].pAttachments = &(img_attachs[i]);
frame_buf_infos[i].width = extent.width;
frame_buf_infos[i].height = extent.height;
frame_buf_infos[i].layers = 1;
vkCreateFramebuffer(vcw_dev.dev, &(frame_buf_infos[i]), NULL, &(vcw_rendp->frame_bufs[i]));
printf("framebuffer %d created.\n", i);
}
vcw_rendp->frame_buf_count = vcw_swap.img_count;
clean_up_sync
for (uint32_t i = 0; i < vcw_sync.max_frames; i++) {
vkDestroySemaphore(vcw_dev.dev, vcw_sync.img_avl_semps[i], NULL);
vkDestroySemaphore(vcw_dev.dev, vcw_sync.rend_fin_semps[i], NULL);
vkDestroyFence(vcw_dev.dev, vcw_sync.fens[i], NULL);
}
free(vcw_sync.img_avl_semps);
free(vcw_sync.rend_fin_semps);
free(vcw_sync.fens);
create_sync
VCW_Sync vcw_sync;
vcw_sync.img_count = vcw_swap.img_count;
vcw_sync.cur_frame = 0;
// THESE ARE MAX FRAMES IN FLIGHT
vcw_sync.max_frames = max_frames_in_flight;
//
// semaphore and fence creation
//
vcw_sync.img_avl_semps = malloc(vcw_sync.max_frames * sizeof(VkSemaphore));
vcw_sync.rend_fin_semps = malloc(vcw_sync.max_frames * sizeof(VkSemaphore));
vcw_sync.fens = malloc(vcw_sync.max_frames * sizeof(VkFence));
VkSemaphoreCreateInfo semp_info;
semp_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semp_info.pNext = NULL;
semp_info.flags = 0;
VkFenceCreateInfo fen_info;
fen_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fen_info.pNext = NULL;
fen_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (uint32_t i = 0; i < vcw_sync.max_frames; i++) {
vkCreateSemaphore(vcw_dev.dev, &semp_info, NULL, &(vcw_sync.img_avl_semps[i]));
vkCreateSemaphore(vcw_dev.dev, &semp_info, NULL, &(vcw_sync.rend_fin_semps[i]));
vkCreateFence(vcw_dev.dev, &fen_info, NULL, &(vcw_sync.fens[i]));
}
printf("semaphores and fences created.\n");
vcw_sync.cur_frame = 0;
vcw_sync.img_fens = malloc(vcw_sync.img_count * sizeof(VkFence));
for (uint32_t i = 0; i < vcw_sync.img_count; i++) {
vcw_sync.img_fens[i] = VK_NULL_HANDLE;
}
return vcw_sync;
clean_up_swap
for (uint32_t i = 0; i < swap.img_count; i++) {
vkDestroyImageView(vcw_dev.dev, swap.img_views[i], NULL);
printf("image view %d destroyed.\n", i);
}
free(swap.img_views);
vkDestroySwapchainKHR(vcw_dev.dev, swap.swap, NULL);
printf("swapchain destroyed.\n");
create_swap
VCW_SWAP = malloc(sizeof(VCW_Swapchain));
//
// create swapchain
//
VkSwapchainCreateInfoKHR swap_info;
swap_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swap_info.pNext = NULL;
swap_info.flags = 0;
swap_info.surface = vcw_surf.surf;
swap_info.minImageCount = vcw_surf.caps.minImageCount + 1;
swap_info.imageFormat = vcw_surf.forms[0].format;
swap_info.imageColorSpace = vcw_surf.forms[0].colorSpace;
VCW_SWAP->extent = vcw_surf.extent_suitable ? vcw_surf.caps.currentExtent : vcw_surf.actual_extent;
swap_info.imageExtent = VCW_SWAP->extent;
swap_info.imageArrayLayers = 1;
swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swap_info.imageSharingMode = vcw_dev.single_queue ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT;
swap_info.queueFamilyIndexCount = vcw_dev.single_queue ? 0 : 2;
uint32_t qf_indices[2] = {0, 1};
swap_info.pQueueFamilyIndices = vcw_dev.single_queue ? NULL : qf_indices;
swap_info.preTransform = vcw_surf.caps.currentTransform;
swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swap_info.presentMode = vcw_surf.mailbox_mode_supported ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_FIFO_KHR;
swap_info.clipped = VK_TRUE;
swap_info.oldSwapchain = old;
vkCreateSwapchainKHR(vcw_dev.dev, &swap_info, NULL, &VCW_SWAP->swap);
printf("swapchain created.\n");
//
// fetch image from swapchain
//
vkGetSwapchainImagesKHR(vcw_dev.dev, VCW_SWAP->swap, &VCW_SWAP->img_count, NULL);
VCW_SWAP->imgs = malloc(VCW_SWAP->img_count * sizeof(VkImage));
vkGetSwapchainImagesKHR(vcw_dev.dev, VCW_SWAP->swap, &VCW_SWAP->img_count, VCW_SWAP->imgs);
printf("%d images fetched from swapchain.\n", VCW_SWAP->img_count);
//
// create image view
//
VCW_SWAP->img_views = malloc(VCW_SWAP->img_count * sizeof(VkImageView));
VkImageViewCreateInfo *img_view_infos = malloc(VCW_SWAP->img_count * sizeof(VkImageViewCreateInfo));
VkImageSubresourceRange subres;
subres = DEFAULT_SUBRESOURCE_RANGE;
subres.layerCount = swap_info.imageArrayLayers;
for (uint32_t i = 0; i < VCW_SWAP->img_count; i++) {
img_view_infos[i].sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
img_view_infos[i].pNext = NULL;
img_view_infos[i].flags = 0;
img_view_infos[i].image = VCW_SWAP->imgs[i];
img_view_infos[i].viewType = VK_IMAGE_VIEW_TYPE_2D;
img_view_infos[i].format = vcw_surf.forms[0].format;
img_view_infos[i].components = DEFAULT_COMPONENT_MAPPING;
img_view_infos[i].subresourceRange = subres;
vkCreateImageView(vcw_dev.dev, &img_view_infos[i], NULL, &VCW_SWAP->img_views[i]);
printf("image view %d created.\n", i);
}
return VCW_SWAP;
clean_up_cmd_pool
vkFreeCommandBuffers(vcw_dev.dev, vcw_cmd.cmd_pool, vcw_cmd.cmd_buf_count, vcw_cmd.cmd_bufs);
free(vcw_cmd.cmd_bufs);
printf("command buffers freed.\n");
vkDestroyCommandPool(vcw_dev.dev, vcw_cmd.cmd_pool, NULL);
printf("command pool destroyed.\n");
create_cmd_pool
VCW_CommandPool vcw_cmd;
//
// create command pool
//
VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = NULL;
cmd_pool_info.flags = 0;
cmd_pool_info.queueFamilyIndex = vcw_dev.qf_best_idx;
vkCreateCommandPool(vcw_dev.dev, &cmd_pool_info, NULL, &vcw_cmd.cmd_pool);
printf("command pool created.\n");
//
// allocate command buffers
//
VkCommandBufferAllocateInfo cmd_alloc_info;
cmd_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd_alloc_info.pNext = NULL;
cmd_alloc_info.commandPool = vcw_cmd.cmd_pool;
cmd_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
vcw_cmd.cmd_buf_count = vcw_swap.img_count;
cmd_alloc_info.commandBufferCount = vcw_cmd.cmd_buf_count;
vcw_cmd.cmd_bufs = malloc(vcw_cmd.cmd_buf_count * sizeof(VkCommandBuffer));
vkAllocateCommandBuffers(vcw_dev.dev, &cmd_alloc_info, vcw_cmd.cmd_bufs);
printf("command buffers allocated.\n");
return vcw_cmd;