Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan Cleanup Validation Error in my application #6812

Closed
Dominik-3D opened this issue Sep 12, 2023 · 5 comments
Closed

Vulkan Cleanup Validation Error in my application #6812

Dominik-3D opened this issue Sep 12, 2023 · 5 comments

Comments

@Dominik-3D
Copy link

Version: Dear ImGui 1.89.9 WIP (18982) + Vulkan & GLFW

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_vulkan.cpp + imgui_impl_glfwcpp .cpp
Compiler: VS2022
Operating System: Win10 64bit

My Issue/Question:

I have a custom class called "Gui" which handels everything ui related and is called form my rendering class. The demo window is shown without any problem and works just finde. However cleanup does not work as expected, it leaves me behind two validation errors:

validation layer: Validation Error: [ VUID-vkDestroyDevice-device-00378 ] Object 0: handle = 0x2b3d6c1ce10, type = VK_OBJECT_TYPE_DEVICE; Object 1: handle = 0x908683000000001d, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0x71500fba | OBJ ERROR : For VkDevice 0x2b3d6c1ce10[], VkDescriptorSet 0x908683000000001d[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.250.0/windows/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-00378)
validation layer: Validation Error: [ VUID-vkDestroyDevice-device-00378 ] Object 0: handle = 0x2b3d6c1ce10, type = VK_OBJECT_TYPE_DEVICE; Object 1: handle = 0xec4bec000000000b, type = VK_OBJECT_TYPE_DESCRIPTOR_POOL; | MessageID = 0x71500fba | OBJ ERROR : For VkDevice 0x2b3d6c1ce10[], VkDescriptorPool 0xec4bec000000000b[] has not been destroyed. The Vulkan spec states: All child objects created on device must have been destroyed prior to destroying device (https://vulkan.lunarg.com/doc/view/1.3.250.0/windows/1.3-extensions/vkspec.html#VUID-vkDestroyDevice-device-00378)

When I remove everthing ui related (meaning that i dont call the init, render, draw, cleanup method), the error is also gone. I tried to track the error down, but everything seems to be pretty much like in the example for Vulkan & GLFW.

Here is the implementation of the "gui" class,:

void Gui::init(GLFWwindow* p_window, VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device,
	uint32_t queueFamily, VkQueue graphicsQueue, std::vector<VkImage> swapChainImages, VkRenderPass renderPass)
{
    #pragma region create descriptor pool
    VkDescriptorPoolSize pool_sizes[] =
    {
        { VK_DESCRIPTOR_TYPE_SAMPLER, 100 },
        { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 100 },
        { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 100 },
        { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 100 },
        { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 100 },
        { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 100 },
        { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 100 },
        { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 100 },
        { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 100 },
        { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 100 },
        { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 100 }
    };
    VkDescriptorPoolCreateInfo pool_info = {};
    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
    pool_info.maxSets = 1;
    pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
    pool_info.pPoolSizes = pool_sizes;
    VkResult err = vkCreateDescriptorPool(device, &pool_info, nullptr, &m_descriptorPool);
    #pragma endregion

    ImGui::CreateContext();
    ImGui::StyleColorsClassic();
    ImGui_ImplGlfw_InitForVulkan(p_window, true);

    ImGui_ImplVulkan_InitInfo init_info = {};
    init_info.Instance = instance;
    init_info.PhysicalDevice = physicalDevice;
    init_info.Device = device;
    init_info.QueueFamily = queueFamily;
    init_info.Queue = graphicsQueue;
    init_info.DescriptorPool = m_descriptorPool;
    init_info.Subpass = 0;
    init_info.MinImageCount = 2;
    init_info.ImageCount = static_cast<unsigned int>(swapChainImages.size());
    init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
    ImGui_ImplVulkan_Init(&init_info, renderPass);
}

void Gui::uploadFonts(VkCommandPool commandPool, VkCommandBuffer commandBuffer, VkQueue graphicsQueue, VkDevice device)
{
    vkResetCommandPool(device, commandPool, 0);
    VkCommandBufferBeginInfo begin_info = {};
    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    vkBeginCommandBuffer(commandBuffer, &begin_info);

    ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);

    VkSubmitInfo end_info = {};
    end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    end_info.commandBufferCount = 1;
    end_info.pCommandBuffers = &commandBuffer;
    vkEndCommandBuffer(commandBuffer);
    vkQueueSubmit(graphicsQueue, 1, &end_info, VK_NULL_HANDLE);

    vkDeviceWaitIdle(device);
    ImGui_ImplVulkan_DestroyFontUploadObjects();
}

void Gui::drawFrame()
{
    ImGui_ImplVulkan_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
    bool showDemo = true;
    ImGui::ShowDemoWindow(&showDemo);
}

void Gui::renderFrame(VkCommandBuffer commandBuffer)
{
    ImGui::Render();
    ImDrawData* drawData = ImGui::GetDrawData();
    ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer);
}

void Gui::cleanup(VkDevice device)
{
    vkDestroyDescriptorPool(device, m_descriptorPool, nullptr);
    ImGui_ImplVulkan_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
}
@GamingMinds-DanielC
Copy link
Contributor

I'm no expert on Vulkan, but noticed two things.

  1. You create the descriptor pool and give it to the initialization functions, you should therefore destroy it after calling the shutdown functions. It is a good practice to handle shutdowns in reverse initialization order. Destroy first what you created last, destroy last what you created first.
  2. Maybe it is in the omitted part of your code, but I don't see a vkDeviceWaitIdle(device) anywhere. Before cleanup (or as the first step of cleanup) you should call that to make sure that no resources are still in use.

@Dominik-3D
Copy link
Author

Thanks for your feedback. The descriptor pool is created in the init function and destroyed in the cleanup function. I already have a vkDeviceWaitIdle(m_device) as first step in my cleanup routine - so that should also be fine. Regarding the order, when I change it to the following, the validation errors stay the same.

void Gui::cleanup(VkDevice device)
{
    ImGui_ImplVulkan_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    vkDestroyDescriptorPool(device, m_descriptorPool, nullptr);
}

@GamingMinds-DanielC
Copy link
Contributor

Just to be sure, did you verify that the problem is in your code first? Does the verification find any problems in the unmodified GLFW Vulkan example? If the original example cleans up without issues, you should try to transport your customizations one by one and check each time.

@ocornut ocornut added the vulkan label Sep 12, 2023
@Dominik-3D
Copy link
Author

Dominik-3D commented Sep 13, 2023

The problem is for sure in my code, but I can't find it anywhere. Basically I'm doing everything identical to the example when it comes to cleanup. So I have really no idea what causes the problem in the cleanup routine. My only idea was, that it is caused by me moving the ui related code to the second class, which should not change anything.

void VulkanRenderer::cleanup(Gui ui)
{
    checkVkResult(vkDeviceWaitIdle(m_device));
    
    ui.cleanup(m_device);
    cleanupSwapChain();

    vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);
    vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
    vkDestroyRenderPass(m_device, m_renderPass, nullptr);

    for (size_t i = 0; i < MAX_FRAMES; i++) {
        vkDestroySemaphore(m_device, m_renderFinishedSemaphores[i], nullptr);
        vkDestroySemaphore(m_device, m_imageAvailableSemaphores[i], nullptr);
        vkDestroyFence(m_device, m_inFlightFences[i], nullptr);
    }

    vkDestroyCommandPool(m_device, m_commandPool, nullptr);

    if (m_enableValidationLayers) {
        auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_instance, "vkDestroyDebugUtilsMessengerEXT");
        if (func != nullptr) {
            func(m_instance, m_debugMessenger, nullptr);
        }
    }

    vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
    vkDestroyDevice(m_device, nullptr);
    vkDestroyInstance(m_instance, nullptr);

    glfwDestroyWindow(p_window);
    glfwTerminate();
}

@ocornut ocornut changed the title Vulkan Cleanup Validation Error Vulkan Cleanup Validation Error in my application Sep 15, 2023
@ocornut
Copy link
Owner

ocornut commented Nov 10, 2023

"Moving code to a second class" should not change anything indeed, assuming things are executed the same way in the same order. If you have a behavioral difference it means you made a mistake.

Please however note, that today we simplified some of the Vulkan backend, so you don't need to create a command-buffer to upload fonts: the backend will do it itself (see comment #6943 (comment) and commit 79a9e2f). Based on this change you should be able to remove your uploadFonts() function completely and let the backend do it automatically.

@ocornut ocornut closed this as completed Dec 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants