You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

simplevk.cpp 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. // Tencent is pleased to support the open source community by making ncnn available.
  2. //
  3. // Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
  4. //
  5. // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  6. // in compliance with the License. You may obtain a copy of the License at
  7. //
  8. // https://opensource.org/licenses/BSD-3-Clause
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed
  11. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13. // specific language governing permissions and limitations under the License.
  14. #include "platform.h"
  15. #if NCNN_VULKAN
  16. #if NCNN_SIMPLEVK
  17. #include "simplevk.h"
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #if defined _WIN32
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #else
  25. #include <dlfcn.h>
  26. #include <sys/types.h>
  27. #include <unistd.h>
  28. #if defined __ANDROID__
  29. #include <dirent.h>
  30. #include <fcntl.h>
  31. #include <sys/stat.h>
  32. #endif
  33. #endif
  34. #if __APPLE__
  35. // always use static vulkan linkage on apple platform
  36. extern "C" VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName);
  37. #endif
  38. namespace ncnn {
  39. // vulkan loader entrypoint
  40. PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = 0;
  41. // vulkan global functions
  42. PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties = 0;
  43. PFN_vkCreateInstance vkCreateInstance = 0;
  44. PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = 0;
  45. #if __APPLE__
  46. int load_vulkan_driver(const char* /*driver_path*/)
  47. {
  48. unload_vulkan_driver();
  49. vkGetInstanceProcAddr = ::vkGetInstanceProcAddr;
  50. vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
  51. vkCreateInstance = (PFN_vkCreateInstance)vkGetInstanceProcAddr(NULL, "vkCreateInstance");
  52. vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
  53. return 0;
  54. }
  55. void unload_vulkan_driver()
  56. {
  57. vkGetInstanceProcAddr = 0;
  58. vkEnumerateInstanceExtensionProperties = 0;
  59. vkCreateInstance = 0;
  60. vkEnumerateInstanceLayerProperties = 0;
  61. }
  62. #else // __APPLE__
  63. #if defined _WIN32
  64. static HMODULE g_libvulkan = 0;
  65. #else
  66. static void* g_libvulkan = 0;
  67. #if defined __ANDROID__
  68. struct hw_module_t;
  69. struct hw_module_methods_t;
  70. struct hw_device_t;
  71. struct hw_module_methods_t
  72. {
  73. /** Open a specific device */
  74. int (*open)(const hw_module_t* mod, const char* id, hw_device_t** device);
  75. };
  76. struct hw_device_t
  77. {
  78. /** tag must be initialized to HARDWARE_DEVICE_TAG */
  79. uint32_t tag;
  80. uint32_t version;
  81. /** reference to the module this device belongs to */
  82. hw_module_t* mod;
  83. /** padding reserved for future use */
  84. #ifdef __LP64__
  85. uint64_t reserved[12];
  86. #else
  87. uint32_t reserved[12];
  88. #endif
  89. /** Close this device */
  90. int (*close)(hw_device_t* device);
  91. };
  92. struct hw_module_t
  93. {
  94. /** tag must be initialized to HARDWARE_MODULE_TAG */
  95. uint32_t tag;
  96. uint16_t module_api_version;
  97. uint16_t hal_api_version;
  98. const char* id;
  99. const char* name;
  100. const char* author;
  101. hw_module_methods_t* methods;
  102. void* dso;
  103. #ifdef __LP64__
  104. uint64_t reserved[32 - 7];
  105. #else
  106. /** padding to 128 bytes, reserved for future use */
  107. uint32_t reserved[32 - 7];
  108. #endif
  109. };
  110. struct hwvulkan_module_t : public hw_module_t
  111. {
  112. };
  113. struct hwvulkan_device_t : public hw_device_t
  114. {
  115. PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties;
  116. PFN_vkCreateInstance CreateInstance;
  117. PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
  118. };
  119. // android hal vulkan loader
  120. static hwvulkan_device_t* g_hvkdi = 0;
  121. #endif
  122. #endif
  123. static std::string get_driver_path_from_icd(const char* icd_path)
  124. {
  125. FILE* fp = fopen(icd_path, "rb");
  126. if (!fp)
  127. return std::string();
  128. std::string driver_path;
  129. char line[256];
  130. while (!feof(fp))
  131. {
  132. char* s = fgets(line, 256, fp);
  133. if (!s)
  134. break;
  135. // "library_path": "path to driver library",
  136. char path[256];
  137. int nscan = sscanf(line, " \"library_path\" : \"%255[^\"]\"", path);
  138. if (nscan == 1)
  139. {
  140. if (path[0] == '.' || (path[0] != '/' && !strchr(path, ':') && (strchr(path, '/') || strchr(path, '\\'))))
  141. {
  142. // relative to the icd file path
  143. std::string icd_dir = icd_path;
  144. size_t dirpos = icd_dir.find_last_of("/\\");
  145. if (dirpos != std::string::npos)
  146. {
  147. icd_dir = icd_dir.substr(0, dirpos + 1);
  148. }
  149. else
  150. {
  151. icd_dir = "./";
  152. }
  153. driver_path = icd_dir + path;
  154. }
  155. else
  156. {
  157. // filename or absolute path
  158. driver_path = path;
  159. }
  160. break;
  161. }
  162. }
  163. fclose(fp);
  164. return driver_path;
  165. }
  166. static std::string get_driver_path_from_icd_env()
  167. {
  168. const char* icd_path = getenv("VK_ICD_FILENAMES");
  169. if (!icd_path)
  170. return std::string();
  171. return get_driver_path_from_icd(icd_path);
  172. }
  173. static std::string get_driver_path_from_ncnn_env()
  174. {
  175. const char* driver_path = getenv("NCNN_VULKAN_DRIVER");
  176. if (!driver_path)
  177. return std::string();
  178. return std::string(driver_path);
  179. }
  180. #if defined _WIN32
  181. static std::string search_file(const std::string& dirpath, const std::string& needle)
  182. {
  183. WIN32_FIND_DATA file;
  184. HANDLE handle = FindFirstFileA((dirpath + "\\*").c_str(), &file);
  185. if (handle == INVALID_HANDLE_VALUE)
  186. return std::string();
  187. int found = 0;
  188. std::vector<std::string> subdirs;
  189. do
  190. {
  191. std::string name = file.cFileName;
  192. if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  193. {
  194. if (name != "." && name != "..")
  195. subdirs.push_back(name);
  196. }
  197. else if (name == needle)
  198. {
  199. found = 1;
  200. break;
  201. }
  202. } while (FindNextFileA(handle, &file));
  203. FindClose(handle);
  204. if (found)
  205. return dirpath + "\\" + needle;
  206. // recurse into subdirs
  207. for (int i = 0; i < subdirs.size(); ++i)
  208. {
  209. std::string found_path = search_file(dirpath + "\\" + subdirs[i], needle);
  210. if (!found_path.empty())
  211. return found_path;
  212. }
  213. return std::string();
  214. }
  215. static int load_vulkan_windows(const char* driver_path)
  216. {
  217. const char* libpath = driver_path ? driver_path : "vulkan-1.dll";
  218. HMODULE libvulkan = LoadLibraryA(libpath);
  219. if (!libvulkan)
  220. {
  221. NCNN_LOGE("LoadLibraryA %s failed %d", libpath, GetLastError());
  222. return -1;
  223. }
  224. PFN_vkGetInstanceProcAddr GetInstanceProcAddr = 0;
  225. GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(libvulkan, "vk_icdGetInstanceProcAddr");
  226. if (GetInstanceProcAddr)
  227. {
  228. // load icd driver
  229. typedef VkResult(VKAPI_PTR * PFN_icdNegotiateLoaderICDInterfaceVersion)(uint32_t * pSupportedVersion);
  230. PFN_icdNegotiateLoaderICDInterfaceVersion icdNegotiateLoaderICDInterfaceVersion = (PFN_icdNegotiateLoaderICDInterfaceVersion)GetProcAddress(libvulkan, "vk_icdNegotiateLoaderICDInterfaceVersion");
  231. if (icdNegotiateLoaderICDInterfaceVersion)
  232. {
  233. uint32_t supported_version = 5;
  234. VkResult ret = icdNegotiateLoaderICDInterfaceVersion(&supported_version);
  235. if (ret != VK_SUCCESS)
  236. {
  237. NCNN_LOGE("icdNegotiateLoaderICDInterfaceVersion failed");
  238. FreeLibrary(libvulkan);
  239. return -1;
  240. }
  241. }
  242. }
  243. else
  244. {
  245. GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(libvulkan, "vkGetInstanceProcAddr");
  246. if (!GetInstanceProcAddr)
  247. {
  248. NCNN_LOGE("GetProcAddress failed %d", GetLastError());
  249. FreeLibrary(libvulkan);
  250. return -1;
  251. }
  252. }
  253. g_libvulkan = libvulkan;
  254. vkGetInstanceProcAddr = GetInstanceProcAddr;
  255. vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
  256. vkCreateInstance = (PFN_vkCreateInstance)vkGetInstanceProcAddr(NULL, "vkCreateInstance");
  257. vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
  258. return 0;
  259. }
  260. #else
  261. static int load_vulkan_linux(const char* driver_path)
  262. {
  263. #if __APPLE__
  264. const char* libpath = driver_path ? driver_path : "libvulkan.dylib";
  265. #else
  266. const char* libpath = driver_path ? driver_path : "libvulkan.so";
  267. #endif
  268. void* libvulkan = dlopen(libpath, RTLD_LOCAL | RTLD_NOW);
  269. if (!libvulkan)
  270. {
  271. NCNN_LOGE("dlopen failed %s", dlerror());
  272. return -1;
  273. }
  274. PFN_vkGetInstanceProcAddr GetInstanceProcAddr = 0;
  275. GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(libvulkan, "vk_icdGetInstanceProcAddr");
  276. if (GetInstanceProcAddr)
  277. {
  278. // load icd driver
  279. typedef VkResult(VKAPI_PTR * PFN_icdNegotiateLoaderICDInterfaceVersion)(uint32_t * pSupportedVersion);
  280. PFN_icdNegotiateLoaderICDInterfaceVersion icdNegotiateLoaderICDInterfaceVersion = (PFN_icdNegotiateLoaderICDInterfaceVersion)dlsym(libvulkan, "vk_icdNegotiateLoaderICDInterfaceVersion");
  281. if (icdNegotiateLoaderICDInterfaceVersion)
  282. {
  283. uint32_t supported_version = 5;
  284. VkResult ret = icdNegotiateLoaderICDInterfaceVersion(&supported_version);
  285. if (ret != VK_SUCCESS)
  286. {
  287. NCNN_LOGE("icdNegotiateLoaderICDInterfaceVersion failed");
  288. dlclose(libvulkan);
  289. return -1;
  290. }
  291. }
  292. }
  293. else
  294. {
  295. GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(libvulkan, "vkGetInstanceProcAddr");
  296. if (!GetInstanceProcAddr)
  297. {
  298. NCNN_LOGE("dlsym failed %s", dlerror());
  299. dlclose(libvulkan);
  300. return -1;
  301. }
  302. }
  303. g_libvulkan = libvulkan;
  304. vkGetInstanceProcAddr = GetInstanceProcAddr;
  305. vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
  306. vkCreateInstance = (PFN_vkCreateInstance)vkGetInstanceProcAddr(NULL, "vkCreateInstance");
  307. vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
  308. return 0;
  309. }
  310. #if defined __ANDROID__
  311. static int load_vulkan_android(const char* driver_path)
  312. {
  313. char hal_driver_path[256];
  314. if (!driver_path)
  315. {
  316. // https://source.android.com/docs/core/graphics/implement-vulkan#driver_emun
  317. // /vendor/lib/hw/vulkan.<ro.hardware.vulkan>.so
  318. // /vendor/lib/hw/vulkan.<ro.product.platform>.so
  319. // /vendor/lib64/hw/vulkan.<ro.hardware.vulkan>.so
  320. // /vendor/lib64/hw/vulkan.<ro.product.platform>.so
  321. #ifdef __LP64__
  322. DIR* d = opendir("/vendor/lib64/hw");
  323. #else
  324. DIR* d = opendir("/vendor/lib/hw");
  325. #endif
  326. if (!d)
  327. return -1;
  328. int hal_driver_found = 0;
  329. struct dirent* dir;
  330. while ((dir = readdir(d)) != NULL)
  331. {
  332. char platform[256];
  333. int nscan = sscanf(dir->d_name, "vulkan.%255s.so", platform);
  334. if (nscan == 1)
  335. {
  336. #ifdef __LP64__
  337. snprintf(hal_driver_path, 256, "/vendor/lib64/hw/%s", dir->d_name);
  338. #else
  339. snprintf(hal_driver_path, 256, "/vendor/lib/hw/%s", dir->d_name);
  340. #endif
  341. hal_driver_found = 1;
  342. break;
  343. }
  344. }
  345. closedir(d);
  346. if (!hal_driver_found)
  347. {
  348. NCNN_LOGE("no hal driver found");
  349. return -1;
  350. }
  351. NCNN_LOGE("hal_driver_path = %s", hal_driver_path);
  352. }
  353. const char* libpath = driver_path ? driver_path : hal_driver_path;
  354. void* libvulkan = dlopen(libpath, RTLD_LOCAL | RTLD_NOW);
  355. if (!libvulkan)
  356. {
  357. NCNN_LOGE("dlopen failed %s", dlerror());
  358. return -1;
  359. }
  360. // resolve entrypoint from android hal module
  361. hw_module_t* hmi = 0;
  362. hmi = (hw_module_t*)dlsym(libvulkan, "HMI");
  363. if (!hmi)
  364. {
  365. NCNN_LOGE("dlsym failed %s", dlerror());
  366. dlclose(libvulkan);
  367. return -1;
  368. }
  369. if (strcmp(hmi->id, "vulkan") != 0)
  370. {
  371. NCNN_LOGE("hmi->id != vulkan");
  372. dlclose(libvulkan);
  373. return -1;
  374. }
  375. hwvulkan_module_t* hvkmi = (hwvulkan_module_t*)hmi;
  376. // NCNN_LOGE("hvkmi name = %s", hvkmi->name);
  377. // NCNN_LOGE("hvkmi author = %s", hvkmi->author);
  378. hwvulkan_device_t* hvkdi = 0;
  379. int result = hvkmi->methods->open(hvkmi, "vk0", (hw_device_t**)&hvkdi);
  380. if (result != 0)
  381. {
  382. NCNN_LOGE("hmi->open failed %d", result);
  383. dlclose(libvulkan);
  384. return -1;
  385. }
  386. g_libvulkan = libvulkan;
  387. g_hvkdi = hvkdi;
  388. vkGetInstanceProcAddr = hvkdi->GetInstanceProcAddr;
  389. vkEnumerateInstanceExtensionProperties = hvkdi->EnumerateInstanceExtensionProperties;
  390. vkCreateInstance = hvkdi->CreateInstance;
  391. vkEnumerateInstanceLayerProperties = (PFN_vkEnumerateInstanceLayerProperties)vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceLayerProperties");
  392. return 0;
  393. }
  394. #endif // __ANDROID__
  395. #endif // _WIN32
  396. int load_vulkan_driver(const char* driver_path)
  397. {
  398. unload_vulkan_driver();
  399. int ret = 0;
  400. std::string driver_path_from_icd_env;
  401. std::string driver_path_from_ncnn_env;
  402. if (driver_path == 0)
  403. {
  404. driver_path_from_icd_env = get_driver_path_from_icd_env();
  405. if (!driver_path_from_icd_env.empty())
  406. {
  407. driver_path = driver_path_from_icd_env.c_str();
  408. }
  409. else
  410. {
  411. driver_path_from_ncnn_env = get_driver_path_from_ncnn_env();
  412. if (!driver_path_from_ncnn_env.empty())
  413. {
  414. driver_path = driver_path_from_ncnn_env.c_str();
  415. }
  416. }
  417. }
  418. // first try, load from driver_path
  419. #if defined _WIN32
  420. ret = load_vulkan_windows(driver_path);
  421. #else
  422. ret = load_vulkan_linux(driver_path);
  423. #if defined __ANDROID__
  424. if (ret != 0)
  425. {
  426. // second try, load from android hal module
  427. ret = load_vulkan_android(driver_path);
  428. }
  429. #endif // __ANDROID__
  430. #endif // _WIN32
  431. if (driver_path != 0 && ret != 0)
  432. {
  433. // third try, load from system vulkan
  434. #if defined _WIN32
  435. ret = load_vulkan_windows(0);
  436. #else
  437. ret = load_vulkan_linux(0);
  438. #if defined __ANDROID__
  439. if (ret != 0)
  440. {
  441. // fourth try, load from any android hal module found
  442. ret = load_vulkan_android(0);
  443. }
  444. #endif // __ANDROID__
  445. #endif // _WIN32
  446. }
  447. if (ret != 0)
  448. {
  449. // fifth try, load from well-known path
  450. #if defined _WIN32
  451. const char* well_known_path[] = {
  452. #if defined(__x86_64__) || defined(_M_X64)
  453. "nvoglv64.dll",
  454. "amdvlk64.dll",
  455. "igvk64.dll"
  456. #else
  457. "nvoglv32.dll",
  458. "amdvlk32.dll",
  459. "igvk32.dll"
  460. #endif
  461. };
  462. #elif defined __ANDROID__
  463. const char* well_known_path[] = {
  464. #ifdef __LP64__
  465. "/vendor/lib64/hw/vulkan.adreno.so",
  466. "/vendor/lib64/egl/libGLES_mali.so"
  467. #else
  468. "/vendor/lib/hw/vulkan.adreno.so",
  469. "/vendor/lib/egl/libGLES_mali.so"
  470. #endif
  471. };
  472. #else
  473. const char* well_known_path[] = {
  474. "libGLX_nvidia.so.0",
  475. "libvulkan_radeon.so",
  476. "libvulkan_intel.so",
  477. "libMaliVulkan.so.1",
  478. "libVK_IMG.so"
  479. };
  480. #endif
  481. const int well_known_path_count = sizeof(well_known_path) / sizeof(const char*);
  482. for (int i = 0; i < well_known_path_count; i++)
  483. {
  484. #if defined _WIN32
  485. // find driver dll in %SystemRoot%\System32\DriverStore\FileRepository (32bit and 64bit both and in here)
  486. std::string dllpath = search_file("%SystemRoot%\\System32\\DriverStore\\FileRepository", well_known_path[i]);
  487. if (dllpath.empty())
  488. continue;
  489. ret = load_vulkan_windows(well_known_path[i]);
  490. #elif defined __ANDROID__
  491. ret = load_vulkan_android(well_known_path[i]);
  492. #else
  493. ret = load_vulkan_linux(well_known_path[i]);
  494. #endif
  495. if (ret == 0)
  496. break;
  497. }
  498. }
  499. return ret;
  500. }
  501. void unload_vulkan_driver()
  502. {
  503. vkGetInstanceProcAddr = 0;
  504. vkEnumerateInstanceExtensionProperties = 0;
  505. vkCreateInstance = 0;
  506. vkEnumerateInstanceLayerProperties = 0;
  507. #if defined _WIN32
  508. if (g_libvulkan)
  509. {
  510. FreeLibrary(g_libvulkan);
  511. g_libvulkan = 0;
  512. }
  513. #else
  514. #if defined __ANDROID__
  515. if (g_hvkdi)
  516. {
  517. if (g_hvkdi->close)
  518. {
  519. g_hvkdi->close(g_hvkdi);
  520. }
  521. g_hvkdi = 0;
  522. }
  523. #endif // __ANDROID__
  524. if (g_libvulkan)
  525. {
  526. dlclose(g_libvulkan);
  527. g_libvulkan = 0;
  528. }
  529. #endif // _WIN32
  530. }
  531. #endif // __APPLE__
  532. } // namespace ncnn
  533. #endif // NCNN_SIMPLEVK
  534. #endif // NCNN_VULKAN