原本你寫的程式是靜態鏈接的系統的vulkan-1.dll,如果系統不存在vulkan-1.dll,則會直接崩潰。 關於將ncnn靜態鏈接vulkan改成動態載入vulkan的形式,然後提供這兩個函數 bool ncnn::has_vulkan(); void ncnn::use_vulkan(boo ...
原本你寫的程式是靜態鏈接的系統的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群里的各位小伙伴吧~