为什么自删除的全局Vulkan实例仅在添加图层时才导致段错误?

时间:2018-06-23 11:39:00

标签: c++ c++14 vulkan raii

我正在使用全局std::shared_ptr来处理对我的Vulkan VkInstance的自动删除。指针具有一个自定义删除器,当它超出范围时会调用vkDestroyInstance。一切正常,直到我启用VK_LAYER_LUNARG_standard_validation层,此时vkDestroyInstance函数会导致段错误。

我在下面添加了一个最小的示例来产生问题。

minimal.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) 

可以通过使用本地实例(在main函数内部)而不是全局实例来解决此问题,因此我想我在使用图层时可能不完全了解Vulkan链接器的某些细微差别。

在我的实际应用程序中,我想使用延迟实例化的静态类来跟踪我的所有Vulkan对象,因此在程序退出时遇到相同的问题。

设置

  • g ++:7.3.0
  • 操作系统:Ubuntu 16.04
  • Nvidia驱动程序:390.67(也尝试过396)
  • Vulkan SDK:1.1.77.0(也尝试过1.1.73)
  • GPU:GeForce GTX TITAN(是否需要双SLI?)

1 个答案:

答案 0 :(得分:4)

全局变量是一个坏主意。在大多数情况下,它们的破坏是无序的。

主要在清理状态,而不是在静态销毁时清理。仅依赖于内存(比POD稍大一点)并且不依赖的简单对象往往不会引起问题,但是如果进一步深入,就会进入大黄蜂的巢。

正在清除您的全局共享ptr,并在清除Vulkan中的任意全局状态后运行销毁代码。这导致了段错误。这里有趣的事情不是“为什么会出现这种段错误”,而是“我如何避免这种段错误”。答案是“停止使用全局状态”。没有其他东西真正起作用。