on
on

三天速成Vulkan(2)-创建VulkanInstance与Validation的挂载

Author:

Vulkan Instance的创建

Vulkan Instance需要通过VkInstanceCreateInfo结构体进行创建,结构体成员如下:

typedef struct VkInstanceCreateInfo {
    VkStructureType             sType; // 指向结构体类型
    const void*                 pNext; // 指向指定的拓展类型的结构体
    VkInstanceCreateFlags       flags; // 保留字段,未使用
    const VkApplicationInfo*    pApplicationInfo; //用于识别应用的额外信息
    uint32_t                    enabledLayerCount; // 需要创建的layer的数目
    const char* const*          ppEnabledLayerNames; // 创建layer对应的名字
    uint32_t                    enabledExtensionCount; // 需要创建拓展的数目
    const char* const*          ppEnabledExtensionNames; // 创建拓展的名字
} VkInstanceCreateInfo;

因此一个Vulkan Instance创建的过程可以用如下表示:

      // 创建AppInfo结构体
      VkApplicationInfo appInfo{};
      appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
      appInfo.pApplicationName = "Hello Triangle";
      appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
      appInfo.pEngineName = "No Engine";
      appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
      appInfo.apiVersion = VK_API_VERSION_1_0;

      VkInstanceCreateInfo createInfo{};
      createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
      createInfo.pApplicationInfo = &appInfo;

      auto extensions = getRequiredExtensions(); // 获取必要 Extensions Names

      createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
      createInfo.ppEnabledExtensionNames = extensions.data();

      // Add validationlayers names to create info;
      if(enableValidationLayers){
        VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;

        createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
        createInfo.ppEnabledLayerNames = validationLayers.data();
        
        // 添加必要 DebugMessenger创建时的结构信息到pNext指针,从而hook上VulkanInstance的创建与析构过程
        populateDebugMessengerCreateInfo(debugCreateInfo);
        createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) & debugCreateInfo;
      }else{
        createInfo.enabledLayerCount = 0;
      }

       // 创建最终的指针
      if(vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS){
        throw std::runtime_error("failed to create instance");
      }

可以看出,Vulkan的创建过程中需要的Extensions的名称列表,以及需要加载的layer列表,extensions与layer相关信息的获取将在接下来说明。
Extensions的生成可参考如下代码,首先 glfw库中取得glfw与vulkan交互所需的Extensions,其为一个const char** 指针,接着将其值赋给std::vector<const char*>,该对象的size即为Extensions的数目。如果需要调用用于debug的Validation layer,再向其中加入VK_EXT_DEBUG_UTILS_EXTENSION_NAME。

    std::vector<const char *> getRequiredExtensions() {
        uint32_t glfwExtensionCount = 0;
        const char **glfwExtensions;

        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
        std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);


        if (enableValidationLayers) {
            extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        }

        return extensions;
    }

另一方面,在添加Validation layers后,我们还可以为其注册我们自己实现的debugCallback,其依托于上面代码中 VK_EXT_debug_utils。我们需要创建一个VkDebugUtilsMessengerEXT对象 debugMessenger,该对象表现为Vulkan Instance下的一个object,其生命周期需要由我们的程序自行管理,即需要在VkInstance析构前销毁该对象。该对象的生成同样需要对应的CreateInfo,具体如下代码所示,相应变量作用请参考代码内注释。

    void populateDebugMessengerCreateInfo(
            VkDebugUtilsMessengerCreateInfoEXT &createInfo) {
        createInfo = {};
        createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
        createInfo.messageSeverity =
                VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
        createInfo.messageType =
                VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
        createInfo.pfnUserCallback = debugCallback;
    }

由于其为依托于我们创建的VkInstance实例下的Object,我们需要从Instance内获取对应的函数指针,获取函数指针并通过结构体创建debugMessenger方法如下:

    VkResult CreateDebugUtilsMessengerEXT(
            VkInstance instance,
            const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,
            const VkAllocationCallbacks *pAllocator,
            VkDebugUtilsMessengerEXT *pDebugMessenger
    ) {
        // Get Vulkan Extension function address
        auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
                                                                               "vkCreateDebugUtilsMessengerEXT");
        if (func != nullptr) {
            return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
        } else {
            return VK_ERROR_EXTENSION_NOT_PRESENT;
        }
    }

    void setupDebugMessenger() {
        if (!enableValidationLayers) return;

        // First: fill the debug messenger creator info struct.
        VkDebugUtilsMessengerCreateInfoEXT createInfo;
        populateDebugMessengerCreateInfo(createInfo);

        if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
            throw std::runtime_error("failed to set up debug messenger");
        }
    }

由于我们需要手动管理该对象,因此释放该对象的方法如下:

    void DestroyDebugUtilsMessengerEXT(
            VkInstance instance,
            VkDebugUtilsMessengerEXT debugMessenger,
            const VkAllocationCallbacks *pAllocator
    ) {
        auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
                                                                                "vkDestroyDebugUtilsMessengerEXT");
        if (func != nullptr) {
            func(instance, debugMessenger, pAllocator);
        }
    }

    void cleanup() {

        // destroy child object before release VkInstance
        if (enableValidationLayers) {
            DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
        }
        vkDestroyInstance(instance, nullptr);

        glfwDestroyWindow(window);
        glfwTerminate();
    }