Почему самоуничтожающийся глобальный экземпляр Vulkan вызывает segfault только при добавлении слоя? - PullRequest
0 голосов
/ 23 июня 2018

Я использую глобальный std::shared_ptr для автоматического удаления моего Vulkan VkInstance.У указателя есть пользовательское средство удаления, которое вызывает vkDestroyInstance, когда выходит из области видимости.Все работает, как и ожидалось, пока я не включу слой VK_LAYER_LUNARG_standard_validation, и в этот момент функция vkDestroyInstance вызывает ошибку segfault.

Ниже я добавил минимальный пример, приводящий к проблеме.

минимальный.cpp

#include <vulkan/vulkan.h>
#include <iostream>
#include <memory>
#include <vector>
#include <cstdlib>

// The global self deleting instance
std::shared_ptr<VkInstance> instance;

int main()
{
    std::vector<const char *> extensions = {VK_EXT_DEBUG_REPORT_EXTENSION_NAME};
    std::vector<const char *> layers = {};
    // Uncomment to cause segfault:
    // layers.emplace_back("VK_LAYER_LUNARG_standard_validation");

    VkApplicationInfo app_info = {};
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app_info.pApplicationName = "Wat";
    app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    app_info.pEngineName = "No Engine";
    app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    app_info.apiVersion = VK_API_VERSION_1_0;

    VkInstanceCreateInfo instance_info = {};
    instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    instance_info.pApplicationInfo = &app_info;
    instance_info.enabledExtensionCount =
        static_cast<uint32_t>(extensions.size());
    instance_info.ppEnabledExtensionNames = extensions.data();
    instance_info.enabledLayerCount = static_cast<uint32_t>(layers.size());
    instance_info.ppEnabledLayerNames = layers.data();

    // Handles auto deletion of the instance when it goes out of scope
    auto deleter = [](VkInstance *pInstance)
    {
        if (*pInstance)
        {
            vkDestroyInstance(*pInstance, nullptr);
            std::cout << "Deleted instance" << std::endl;
        }
        delete pInstance;
    };

    instance = std::shared_ptr<VkInstance>(new VkInstance(nullptr), deleter);
    if (vkCreateInstance(&instance_info, nullptr, instance.get()) != VK_SUCCESS)
    {
        std::cerr << "Failed to create a Vulkan instance" << std::endl;
        return EXIT_FAILURE;
    }
    std::cout << "Created instance" << std::endl;

    // When the program exits, everything should clean up nicely?
    return EXIT_SUCCESS;
}

При запуске вышеуказанной программы, как есть, я получаю то, что ожидаю:

$ g++-7 -std=c++14 minimal.cpp -isystem $VULKAN_SDK/include -L$VULKAN_SDK/lib -lvulkan -o minimal
$ ./minimal 
Created instance
Deleted instance
$

Однако, как только я добавлю строку VK_LAYER_LUNARG_standard_validation:

// Uncomment to cause segfault:
layers.emplace_back("VK_LAYER_LUNARG_standard_validation");

Я получаю

$ g++-7 -std=c++14 minimal.cpp -isystem $VULKAN_SDK/include -L$VULKAN_SDK/lib -lvulkan -o minimal
$ ./minimal 
Created instance
Segmentation fault (core dumped)
$

При запуске с gdb обратная трассировка показывает ошибку, происходящую в функции VkDeleteInstance:

$ g++-7 -std=c++14 -g minimal.cpp -isystem $VULKAN_SDK/include -L$VULKAN_SDK/lib -lvulkan -o minimal
$ gdb -ex run ./minimal 
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
...
Starting program: /my/path/stackoverflow/vulkan/minimal 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Created instance

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff24c4334 in threading::DestroyInstance(VkInstance_T*, VkAllocationCallbacks const*) () from /my/path/Vulkan/1.1.77.0/x86_64/lib/libVkLayer_threading.so
(gdb) bt
#0  0x00007ffff24c4334 in threading::DestroyInstance(VkInstance_T*, VkAllocationCallbacks const*) () from /my/path/Vulkan/1.1.77.0/x86_64/lib/libVkLayer_threading.so
#1  0x00007ffff7bad243 in vkDestroyInstance () from /my/path/Vulkan/1.1.77.0/x86_64/lib/libvulkan.so.1
#2  0x000000000040105c in <lambda(VkInstance_T**)>::operator()(VkInstance *) const (__closure=0x617c90, pInstance=0x617c60) at minimal.cpp:38
#3  0x000000000040199a in std::_Sp_counted_deleter<VkInstance_T**, main()::<lambda(VkInstance_T**)>, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose(void) (this=0x617c80) at /usr/include/c++/7/bits/shared_ptr_base.h:470
#4  0x0000000000401ef0 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x617c80) at /usr/include/c++/7/bits/shared_ptr_base.h:154
#5  0x0000000000401bc7 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x6052d8 <instance+8>, __in_chrg=<optimized out>) at /usr/include/c++/7/bits/shared_ptr_base.h:684
#6  0x0000000000401b6a in std::__shared_ptr<VkInstance_T*, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x6052d0 <instance>, __in_chrg=<optimized out>) at /usr/include/c++/7/bits/shared_ptr_base.h:1123
#7  0x0000000000401b9c in std::shared_ptr<VkInstance_T*>::~shared_ptr (this=0x6052d0 <instance>, __in_chrg=<optimized out>) at /usr/include/c++/7/bits/shared_ptr.h:93
#8  0x00007ffff724bff8 in __run_exit_handlers (status=0, listp=0x7ffff75d65f8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82
#9  0x00007ffff724c045 in __GI_exit (status=<optimized out>) at exit.c:104
#10 0x00007ffff7232837 in __libc_start_main (main=0x40108c <main()>, argc=1, argv=0x7fffffffdcf8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdce8) at ../csu/libc-start.c:325
#11 0x0000000000400ed9 in _start ()
(gdb) 

Проблема может бытьисправлено с помощью локального экземпляра (внутри основной функции) вместо глобального, поэтому я думаю, что я не совсем понимаю некоторые нюансы компоновщика Vulkan при использовании слоев.

В моем реальном приложении я хочуиспользуйте ленивый экземплярный статический класс, чтобы отслеживать все мои объекты Vulkan, и поэтому я сталкиваюсь с той же проблемой при выходе из программы.

Setup

  • g ++: 7.3.0
  • ОС: Ubuntu 16.04
  • Nvidia Драйвер: 390,67 (также пробовал 396)
  • Vulkan SDK: 1.1.77.0 (также пробовал 1.1.73)
  • GPU: GeForce GTX TITAN (Dual SLI, если это имеет значение?)

1 Ответ

0 голосов
/ 23 июня 2018

Глобальные переменные - плохая идея.Их разрушение в большинстве случаев неупорядочено относительно друг друга.

Очистите свое состояние в основном, а не во время статического разрушения.Простые объекты, которые зависят только от памяти (небольшой шаг от POD) и не пересекаются с зависимостью, как правило, не вызывают проблем, но идут дальше, и вы попадаете в гнездо шершня.

Ваш глобальный общий ресурс находитсяочищается, и код уничтожения, запускаемый после некоторого произвольного глобального состояния в Vulkan, очищается.Это вызывает segfault.Интересно, что здесь не «почему это сегфо», а «как я могу избежать такого рода сегфо».Ответ на это "прекратить использование глобального состояния";больше ничего не работает.

...