轉載請注明:http://www.cnblogs.com/vertexshader/articles/5225675.html,歡迎入群54288273一起扯淡。
前序的什么環境配置等工作就不總結了,所有的資料和SDK都匯總在這個地址,按照要求安裝和配置就可以了。
吐槽優先!
萬事開頭先吐槽,Vulkan SDK簡直就是日了狗了我想說,函數的名稱終於達到了又長又臭的地步,以至於Demo代碼看起來是這樣的:
1 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
一個函數的名稱就達到了35個字符,這還是短的,還有更長的,加加參數一個函數就可以占幾行了。
到底做了啥?
這次Vulkan不像OpenGL一樣是無SDK的,它妥妥的出了一個叫做Vulkan SDK的東西,似乎我們只需要include這個頭文件,link這些lib就接入了Vulkan的SDK。不過我在Demo里看到了一些函數仍然需要通過vkGetInstanceProcAddr的方式獲取函數指針,這時候我就對這個SDK的行為產生了懷疑和興趣。
回顧一下以前了解OpenGL的經驗,OpenGL Runtime是由不同的Manufactory所實現的ICD(Installable Client Driver)來支持的。通過載入默認OpenGL庫的方式,獲取相應的函數指針,完成整個API的初始化的。這個不同於Direct3D,由於Direct3D的封閉性,其Runtime是在操作系統層面實現的。而Vulkan又是OpenGL的繼承者,又是由一大堆的Manufactory在背后默默地支持,可以充分地懷疑其應該是通過和過去OpenGL類似的方式獲取函數指針實現的。
還好Vulkan SDK是開源的,果然不假,搜索整個代碼在vk_loader_platform.h發現了:
typedef HMODULE loader_platform_dl_handle; static loader_platform_dl_handle loader_platform_open_library(const char *libPath) { return LoadLibrary(libPath); }
打上斷點,看看搞了毛:
loader_platform_open_library(const char * libPath) loader_scanned_icd_add(...) loader_icd_scan(...) vkCreateInstance(...) main()
而這時候libPath的值是“C:\\Windows\\System32\\nvoglv32.dll”,追溯上游,發現其在函數vkCreateInstance中調用了loader_icd_scan這個接口,而這個接口又調用了loader_get_manifest_files這個接口,
loader_get_manifest_files內部有一代碼是:
loc = loader_get_registry_files(inst, loc);
這個loader_get_registry_files則是調用了RegOpenKeyEx這個接口,也就是從注冊表中獲得鍵值,而loc的值則是預定義的宏“SOFTWARE\\Khronos\\Vulkan\\Drivers”。打開注冊表,看看這個值到底是個啥哇:
指向了一個json文件,打開這個json文件看看其中內容:
{ "file_format_version" : "1.0.0", "ICD": { "library_path": "nvoglv64.dll", "api_version" : "1.0.4" } }
原來被騙了!
之前的json文件原來指定了庫的名稱和API的版本,那么nv-vk64.dll和nvoglv64.dll到底有什么接口呢,可以直接打開dll查看其接口和偏移:
是不是有很多vk的接口?但是只有一個接口是核心的,就是vk_icdGetInstanceProcAddr這個接口,在后續的loader_scanned_icd_add調用中,代碼從載入的庫中獲得了這個函數指針,然后干了一件事情:
fp_create_inst = (PFN_vkCreateInstance)fp_get_proc_addr(NULL, "vkCreateInstance");
原來是創建了入口的函數指針實例!說好的vkCreateInstance接口,也就是包在外面的一層,並不是真正的vkCreateInstance。那么也就是說整個流程就是,Vulkan Loader掃描注冊表獲得相應的json文件,載入這個json文件,載入dll,獲得入口函數指針,載入相應函數指針。也就是說這套方法仍然和過去OpenGL的方法一致。而這個載入過程,通過檢查函數調用,也會在vkEnumerateInstanceExtensionProperties等接口中調用。而SDK中的接口,都是對函數指針包了一層,例如vkCreateBuffer:
LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) { const VkLayerDispatchTable *disp; disp = loader_get_dispatch(device); return disp->CreateBuffer(device, pCreateInfo, pAllocator, pBuffer); }
整個VkLayerDispatchTable就是函數指針的集合。
為啥這么干?
那么這么做的意義是什么呢?直接調用這些函數指針不就完事了嗎?恰巧vulkan的一個思想就是,去除掉驗證層,那么驗證層能放在何處?也就是放在這個SDK里,類似各種Graphic Debugger的實現,Vulkan有很多Validation Layer,可以根據需求載入,在真正的函數調用之前,截取一些信息,獲得一些信息,最后再調用真正的接口。也就是說整個SDK不僅僅是一個函數指針獲取器,還是一個Graphic Debugger。