From c8cfac56785134f92c83ed37d98feade770b6af3 Mon Sep 17 00:00:00 2001 From: Dennis Brakhane Date: Fri, 18 Aug 2023 17:20:30 +0200 Subject: [PATCH] Various vulkan fixes (#732) * vulkan: no need to explicitly enable VK_KHR_get_physical_device_properties2 It has been promoted to core Vulkan 1.1 * vulkan: fix creating and destroying swapchains We can't reuse a semaphore if it is signalled. There is no easy way to wait on the swapchain semaphore without some ugly hacks, so instead we just create a new one. We also need make sure we only destroy old swapchains once they are (probably) not used anymore. The recent VK_EXT_swapchain_maintenance1 extension would make all of this much easier, but as of now, it's not yet widely supported. * cleanup: reduce amount of copy-paste in AllocationHandler::Update() Using a lambda allows us to remove the copy-pasted loop and increase legibility by reducing the code by almost two thirds with no performance impact: both GCC and MSVC generate almost the same code as in the old version, the differences being often due to slightly different register allocations. * vulkan: ensure render area is not greater than color attachments swapchain->desc.width/height can differ from the swapChainExtent, for example if a VK_KHR_SUBOPTIMAL happened before we received the "window size changed" event, or if it is not between the minimal or maximal extents. Ensure we don't use a render area larger than the color attachments, as this violates VUID-VkRenderingInfo-pNext-06079 and 06080. --- WickedEngine/wiGraphicsDevice_Vulkan.cpp | 32 +- WickedEngine/wiGraphicsDevice_Vulkan.h | 436 +++++------------------ 2 files changed, 118 insertions(+), 350 deletions(-) diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 7a79c1b93..70b073e71 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -1170,7 +1170,10 @@ namespace vulkan_internal // the color space change will not be applied res = vkDeviceWaitIdle(device); assert(res == VK_SUCCESS); - vkDestroySwapchainKHR(device, internal_state->swapChain, nullptr); + { + std::scoped_lock lock(allocationhandler->destroylocker); + allocationhandler->destroyer_swapchains.emplace_back(internal_state->swapChain, allocationhandler->framecount); + } internal_state->swapChain = nullptr; } } @@ -1230,7 +1233,8 @@ namespace vulkan_internal if (createInfo.oldSwapchain != VK_NULL_HANDLE) { - vkDestroySwapchainKHR(device, createInfo.oldSwapchain, nullptr); + std::scoped_lock lock(allocationhandler->destroylocker); + allocationhandler->destroyer_swapchains.emplace_back(createInfo.oldSwapchain, allocationhandler->framecount); } res = vkGetSwapchainImagesKHR(device, internal_state->swapChain, &imageCount, nullptr); @@ -2373,16 +2377,12 @@ using namespace vulkan_internal; debugUtils = true; instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - else if (strcmp(availableExtension.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == 0) - { - instanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); - } else if (strcmp(availableExtension.extensionName, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME) == 0) { instanceExtensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); } } - + instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); #if defined(VK_USE_PLATFORM_WIN32_KHR) @@ -7404,13 +7404,12 @@ using namespace vulkan_internal; commandlist.renderpass_barriers_begin.clear(); commandlist.renderpass_barriers_end.clear(); auto internal_state = to_internal(swapchain); - commandlist.prev_swapchains.push_back(*swapchain); internal_state->locker.lock(); VkResult res = vkAcquireNextImageKHR( device, internal_state->swapChain, - 0xFFFFFFFFFFFFFFFF, + UINT64_MAX, internal_state->swapchainAcquireSemaphore, VK_NULL_HANDLE, &internal_state->swapChainImageIndex @@ -7422,6 +7421,16 @@ using namespace vulkan_internal; // Handle outdated error in acquire: if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { + // we need to create a new semaphore or jump through a few hoops to + // wait for the current one to be unsignalled before we can use it again + // creating a new one is easiest. See also: + // https://github.com/KhronosGroup/Vulkan-Docs/issues/152 + // https://www.khronos.org/blog/resolving-longstanding-issues-with-wsi + { + std::scoped_lock lock(allocationhandler->destroylocker); + allocationhandler->destroyer_semaphores.emplace_back(internal_state->swapchainAcquireSemaphore, allocationhandler->framecount); + } + internal_state->swapchainAcquireSemaphore = VK_NULL_HANDLE; if (CreateSwapChainInternal(internal_state, physicalDevice, device, allocationhandler)) { RenderPassBegin(swapchain, cmd); @@ -7430,13 +7439,14 @@ using namespace vulkan_internal; } assert(0); } + commandlist.prev_swapchains.push_back(*swapchain); VkRenderingInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; info.renderArea.offset.x = 0; info.renderArea.offset.y = 0; - info.renderArea.extent.width = swapchain->desc.width; - info.renderArea.extent.height = swapchain->desc.height; + info.renderArea.extent.width = std::min(swapchain->desc.width, internal_state->swapChainExtent.width); + info.renderArea.extent.height = std::min(swapchain->desc.height, internal_state->swapChainExtent.height); info.layerCount = 1; VkRenderingAttachmentInfo color_attachment = {}; diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.h b/WickedEngine/wiGraphicsDevice_Vulkan.h index fc76549c1..ea1c0f666 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.h +++ b/WickedEngine/wiGraphicsDevice_Vulkan.h @@ -595,346 +595,104 @@ namespace wi::graphics // Deferred destroy of resources that the GPU is already finished with: void Update(uint64_t FRAMECOUNT, uint32_t BUFFERCOUNT) { - destroylocker.lock(); + const auto destroy = [&](auto&& queue, auto&& handler) { + while (!queue.empty()) { + if (queue.front().second + BUFFERCOUNT < FRAMECOUNT) + { + auto item = queue.front(); + queue.pop_front(); + handler(item.first); + } + else + { + break; + } + } + }; + framecount = FRAMECOUNT; - while (!destroyer_allocations.empty()) - { - if (destroyer_allocations.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_allocations.front(); - destroyer_allocations.pop_front(); - vmaFreeMemory(allocator, item.first); - } - else - { - break; - } - } - while (!destroyer_images.empty()) - { - if (destroyer_images.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_images.front(); - destroyer_images.pop_front(); - vmaDestroyImage(allocator, item.first.first, item.first.second); - } - else - { - break; - } - } - while (!destroyer_imageviews.empty()) - { - if (destroyer_imageviews.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_imageviews.front(); - destroyer_imageviews.pop_front(); - vkDestroyImageView(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_buffers.empty()) - { - if (destroyer_buffers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_buffers.front(); - destroyer_buffers.pop_front(); - vmaDestroyBuffer(allocator, item.first.first, item.first.second); - } - else - { - break; - } - } - while (!destroyer_bufferviews.empty()) - { - if (destroyer_bufferviews.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_bufferviews.front(); - destroyer_bufferviews.pop_front(); - vkDestroyBufferView(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_bvhs.empty()) - { - if (destroyer_bvhs.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_bvhs.front(); - destroyer_bvhs.pop_front(); - vkDestroyAccelerationStructureKHR(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_samplers.empty()) - { - if (destroyer_samplers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_samplers.front(); - destroyer_samplers.pop_front(); - vkDestroySampler(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_descriptorPools.empty()) - { - if (destroyer_descriptorPools.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_descriptorPools.front(); - destroyer_descriptorPools.pop_front(); - vkDestroyDescriptorPool(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_descriptorSetLayouts.empty()) - { - if (destroyer_descriptorSetLayouts.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_descriptorSetLayouts.front(); - destroyer_descriptorSetLayouts.pop_front(); - vkDestroyDescriptorSetLayout(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_descriptorUpdateTemplates.empty()) - { - if (destroyer_descriptorUpdateTemplates.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_descriptorUpdateTemplates.front(); - destroyer_descriptorUpdateTemplates.pop_front(); - vkDestroyDescriptorUpdateTemplate(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_shadermodules.empty()) - { - if (destroyer_shadermodules.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_shadermodules.front(); - destroyer_shadermodules.pop_front(); - vkDestroyShaderModule(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_pipelineLayouts.empty()) - { - if (destroyer_pipelineLayouts.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_pipelineLayouts.front(); - destroyer_pipelineLayouts.pop_front(); - vkDestroyPipelineLayout(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_pipelines.empty()) - { - if (destroyer_pipelines.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_pipelines.front(); - destroyer_pipelines.pop_front(); - vkDestroyPipeline(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_querypools.empty()) - { - if (destroyer_querypools.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_querypools.front(); - destroyer_querypools.pop_front(); - vkDestroyQueryPool(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_swapchains.empty()) - { - if (destroyer_swapchains.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_swapchains.front(); - destroyer_swapchains.pop_front(); - vkDestroySwapchainKHR(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_surfaces.empty()) - { - if (destroyer_surfaces.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_surfaces.front(); - destroyer_surfaces.pop_front(); - vkDestroySurfaceKHR(instance, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_semaphores.empty()) - { - if (destroyer_semaphores.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_semaphores.front(); - destroyer_semaphores.pop_front(); - vkDestroySemaphore(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_video_sessions.empty()) - { - if (destroyer_video_sessions.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_video_sessions.front(); - destroyer_video_sessions.pop_front(); - vkDestroyVideoSessionKHR(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_video_session_parameters.empty()) - { - if (destroyer_video_session_parameters.front().second + BUFFERCOUNT < FRAMECOUNT) - { - auto item = destroyer_video_session_parameters.front(); - destroyer_video_session_parameters.pop_front(); - vkDestroyVideoSessionParametersKHR(device, item.first, nullptr); - } - else - { - break; - } - } - while (!destroyer_bindlessSampledImages.empty()) - { - if (destroyer_bindlessSampledImages.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessSampledImages.front().first; - destroyer_bindlessSampledImages.pop_front(); - bindlessSampledImages.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessUniformTexelBuffers.empty()) - { - if (destroyer_bindlessUniformTexelBuffers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessUniformTexelBuffers.front().first; - destroyer_bindlessUniformTexelBuffers.pop_front(); - bindlessUniformTexelBuffers.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessStorageBuffers.empty()) - { - if (destroyer_bindlessStorageBuffers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessStorageBuffers.front().first; - destroyer_bindlessStorageBuffers.pop_front(); - bindlessStorageBuffers.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessStorageImages.empty()) - { - if (destroyer_bindlessStorageImages.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessStorageImages.front().first; - destroyer_bindlessStorageImages.pop_front(); - bindlessStorageImages.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessStorageTexelBuffers.empty()) - { - if (destroyer_bindlessStorageTexelBuffers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessStorageTexelBuffers.front().first; - destroyer_bindlessStorageTexelBuffers.pop_front(); - bindlessStorageTexelBuffers.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessSamplers.empty()) - { - if (destroyer_bindlessSamplers.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessSamplers.front().first; - destroyer_bindlessSamplers.pop_front(); - bindlessSamplers.free(index); - } - else - { - break; - } - } - while (!destroyer_bindlessAccelerationStructures.empty()) - { - if (destroyer_bindlessAccelerationStructures.front().second + BUFFERCOUNT < FRAMECOUNT) - { - int index = destroyer_bindlessAccelerationStructures.front().first; - destroyer_bindlessAccelerationStructures.pop_front(); - bindlessAccelerationStructures.free(index); - } - else - { - break; - } - } + + destroylocker.lock(); + + destroy(destroyer_allocations, [&](auto& item) { + vmaFreeMemory(allocator, item); + }); + destroy(destroyer_images, [&](auto& item) { + vmaDestroyImage(allocator, item.first, item.second); + }); + destroy(destroyer_imageviews, [&](auto& item) { + vkDestroyImageView(device, item, nullptr); + }); + destroy(destroyer_buffers, [&](auto& item) { + vmaDestroyBuffer(allocator, item.first, item.second); + }); + destroy(destroyer_bufferviews, [&](auto& item) { + vkDestroyBufferView(device, item, nullptr); + }); + destroy(destroyer_bvhs, [&](auto& item) { + vkDestroyAccelerationStructureKHR(device, item, nullptr); + }); + destroy(destroyer_samplers, [&](auto& item) { + vkDestroySampler(device, item, nullptr); + }); + destroy(destroyer_descriptorPools, [&](auto& item) { + vkDestroyDescriptorPool(device, item, nullptr); + }); + destroy(destroyer_descriptorSetLayouts, [&](auto& item) { + vkDestroyDescriptorSetLayout(device, item, nullptr); + }); + destroy(destroyer_descriptorUpdateTemplates, [&](auto& item) { + vkDestroyDescriptorUpdateTemplate(device, item, nullptr); + }); + destroy(destroyer_shadermodules, [&](auto& item) { + vkDestroyShaderModule(device, item, nullptr); + }); + destroy(destroyer_pipelineLayouts, [&](auto& item) { + vkDestroyPipelineLayout(device, item, nullptr); + }); + destroy(destroyer_pipelines, [&](auto& item) { + vkDestroyPipeline(device, item, nullptr); + }); + destroy(destroyer_querypools, [&](auto& item) { + vkDestroyQueryPool(device, item, nullptr); + }); + destroy(destroyer_swapchains, [&](auto& item) { + vkDestroySwapchainKHR(device, item, nullptr); + }); + destroy(destroyer_surfaces, [&](auto& item) { + vkDestroySurfaceKHR(instance, item, nullptr); + }); + destroy(destroyer_semaphores, [&](auto& item) { + vkDestroySemaphore(device, item, nullptr); + }); + destroy(destroyer_video_sessions, [&](auto& item) { + vkDestroyVideoSessionKHR(device, item, nullptr); + }); + destroy(destroyer_video_session_parameters, [&](auto& item) { + vkDestroyVideoSessionParametersKHR(device, item, nullptr); + }); + destroy(destroyer_bindlessSampledImages, [&](auto& item) { + bindlessSampledImages.free(item); + }); + destroy(destroyer_bindlessUniformTexelBuffers, [&](auto& item) { + bindlessUniformTexelBuffers.free(item); + }); + destroy(destroyer_bindlessStorageBuffers, [&](auto& item) { + bindlessStorageBuffers.free(item); + }); + destroy(destroyer_bindlessStorageImages, [&](auto& item) { + bindlessStorageImages.free(item); + }); + destroy(destroyer_bindlessStorageTexelBuffers, [&](auto& item) { + bindlessStorageTexelBuffers.free(item); + }); + destroy(destroyer_bindlessSamplers, [&](auto& item) { + bindlessSamplers.free(item); + }); + destroy(destroyer_bindlessAccelerationStructures, [&](auto& item) { + bindlessAccelerationStructures.free(item); + }); + destroylocker.unlock(); } };