Reputation: 179
I am using a global std::shared_ptr
to handle automatic deletion of my Vulkan VkInstance
. The pointer has a custom deleter that calls vkDestroyInstance
when it goes out of scope. Everything works as expected until I enable the VK_LAYER_LUNARG_standard_validation
layer at which point the vkDestroyInstance
function causes a segfault.
I have added a minimal example below that produces the issue.
#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;
}
When running the above program as is, the output is what I would expect:
$ g++-7 -std=c++14 minimal.cpp -isystem $VULKAN_SDK/include -L$VULKAN_SDK/lib -lvulkan -o minimal
$ ./minimal
Created instance
Deleted instance
$
However as soon as I add back the VK_LAYER_LUNARG_standard_validation
line:
// Uncomment to cause segfault:
layers.emplace_back("VK_LAYER_LUNARG_standard_validation");
I get
$ 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)
$
When run with gdb
the backtrace shows the segfault occurring in the VkDeleteInstance
function:
$ 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)
The problem can be fixed by using a local instance (inside the main function) instead of a global one so I'm thinking I might not fully understand some nuances of the Vulkan linker when using layers.
In my actual application I want to use a lazily instantiated static class to keep track of all my Vulkan objects and so I run into the same problem when the program exits.
Upvotes: 1
Views: 1485
Reputation: 275370
Global variables are a bad idea. Their destruction is unordered relative to each other in most cases.
Clean up your state in main, not at static destruction time. Simple objects that depend only on memory (a small step up from POD) and don't cross depend tend not to cause problems, but go any further and you enter a hornet's nest.
Your global shared ptr is being cleared and the destruction code run after some arbitrary global state within Vulkan is being cleared. This is causing a segfault. The interesting thing here isn't "why this segfault" but rather "how can I avoid this kind of segfault". The answer to that is "stop using global state"; nothing else really works.
Upvotes: 5