Freeze during window resize

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

Link to Github repo

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;

Ok I fixed it by removing AddressSanitizer from CMake.