使用Vulkan-Loader将ncnn代码改成Dynamic Loader Vulkan的形式

原本你写的程序是静态链接的系统的vulkan-1.dll,如果系统不存在vulkan-1.dll,则会直接崩溃。

关于将ncnn静态链接vulkan改成动态加载vulkan的形式,然后提供这两个函数

bool ncnn::has_vulkan();
void ncnn::use_vulkan(bool);

请教过ncnn的作者nihui,她对此issue表示不以为意,没有必要,优先级不高。

那就只有自己动手丰衣足食了。

本文的目标是将其改为动态加载的方式,用到了KhronosGroup组织下的Vulkan-SDK里面的CPP部分,即vulkan.hpp

本人的上一篇文章(https://www.cnblogs.com/hyb1/p/17361775.html)说的是如何动态判断是否存在vulkan-1.dll并且再加载的过程,

但是没提到如何针对现有的项目改动,比如说现在的ncnn含有的大量C Style的vulkan函数符号直接调用的代码,如果直接修改,需要将每一个vulkan函数的地方都加一个前缀,类似这样:

mVulkanDispatchLoaderDynamic->vkCreateInstance(&instanceCreateInfo, 0, &instance);

mVulkanDispatchLoaderDynamic->vkEnumeratePhysicalDevices(g_instance, &physicalDeviceCount, 0);

跨越了很多.cpp和.h文件,修改起来相当麻烦,而且还会遇到全局变量是否有效等等问题。

废话不多说,直接说总结:

Vulkan Loader提供了多个宏定义:VULKAN_HPP_STORAGE_API和VULKAN_HPP_STORAGE_SHARED和VULKAN_HPP_STORAGE_SHARED_EXPORT
它们是vulkan.hpp中用来控制vulkan函数的存储类别的宏。
VULKAN_HPP_STORAGE_API:

这个宏用来指定vulkan函数的存储类别,比如__declspec(dllexport)或者__declspec(dllimport)。
这个宏可以在编译时由外部定义,以便于将vulkan函数导出或者导入。


VULKAN_HPP_STORAGE_SHARED:

这个宏用来启用动态链接库的模式,即将vulkan函数作为dll的导出或者导入。
如果定义了这个宏,那么VULKAN_HPP_STORAGE_API会根据VULKAN_HPP_STORAGE_SHARED_EXPORT是否定义来自动设置为__declspec(dllexport)或者__declspec(dllimport)。
如果没有定义这个宏,那么VULKAN_HPP_STORAGE_API会被设置为空。


VULKAN_HPP_STORAGE_SHARED_EXPORT:

这个宏用来控制动态链接库的模式下,vulkan函数是作为dll的导出还是导入。
如果定义了这个宏,那么VULKAN_HPP_STORAGE_API会被设置为__declspec(dllexport),表示vulkan函数是dll的导出。

如果没有定义这个宏,那么VULKAN_HPP_STORAGE_API会被设置为__declspec(dllimport),表示vulkan函数是dll的导入。

如果你是希望通过vk::DispatchLoaderDynamic加载到defaultDispatchLoaderDynamic之后,仍旧按照静态链接的模式来编写代码,但是不是链接到真正的vulkan-1.dll上,而是由vulkan-loader生成的符号,应该这样做(重点来了):

1. 定义VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL和VULKAN_HPP_DISPATCH_LOADER_DYNAMIC两个宏,以启用动态加载vulkan库和函数的功能。
2. 不要定义VULKAN_HPP_STORAGE_SHARED和VULKAN_HPP_STORAGE_SHARED_EXPORT两个宏,以避免使用dll的模式。
3. 使用vk::DynamicLoader类来动态加载vulkan-loader生成的符号,并且获取vkGetInstanceProcAddr函数的地址。
4. 调用VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr)来初始化默认的函数指针封装对象。
5. 使用默认的函数指针封装对象或者自定义的vk::DispatchLoaderDynamic对象来调用其他vulkan函数。

#define VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL 1
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
//#define VULKAN_HPP_STORAGE_API 0
#undef VULKAN_HPP_STORAGE_SHARED
#undef VULKAN_HPP_STORAGE_SHARED_EXPORT

#include <vulkan/vulkan.hpp>

//定义一个全局的vk::DispatchLoaderDynamic对象defaultDispatchLoaderDynamic
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE


//初始化的时候这样写
try
{
#ifdef WIN32
    std::string vulkanLibraryFilePath = "vulkan-1.dll";
#else //APPLE
    std::string vulkanLibraryFilePath = diropt::CurrentPath() + "/Contents/Frameworks/libMoltenVK.dylib";
#endif
    mVulkanDynamicLoader = std::make_unique<vk::DynamicLoader>(vulkanLibraryFilePath);
}
catch(std::runtime_error ex)
{
    mSupportVulkan = false;
    printf("vk::DynamicLoader is null\n");
    return;
}
if(!mVulkanDynamicLoader)
{
    mSupportVulkan = false;
    printf("mVulkanDynamicLoader is null\n");
    return;
}
mSupportVulkan = mVulkanDynamicLoader->success();
if(!mSupportVulkan)
{
    mSupportVulkan = false;
    printf("mSupportVulkan is false\n");
    return;
}
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = mVulkanDynamicLoader->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
if(!vkGetInstanceProcAddr)
{
    mSupportVulkan = false;
    printf("vkGetInstanceProcAddr is null\n");
    return;
}

mVulkanDispatchLoaderDynamic = std::make_unique<vk::DispatchLoaderDynamic>(vkGetInstanceProcAddr);
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);

 

只要这样按照这上面的5点来修改ncnn的话,就可以编译出动态加载vulkan版本的ncnn了,如果你看到了这里觉得对你有帮助的话,就分享到qq群里的各位小伙伴吧~

 

posted @ 2023-09-14 15:55  重庆Debug  阅读(163)  评论(0编辑  收藏  举报