Vulkan:初始化 前篇

转载请注明: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。

posted @ 2016-03-12 14:03 YangZhao1992 阅读(...) 评论(...) 编辑 收藏