Vulkan API 调用 GPU 驱动程序

问题描述

背景:
我一直着眼于编写一个需要非常基本但快速的图形(仅绘制线条和正方形)的应用程序,如果我要使用 Rust,我可能会使用 GLFW 或 Vulkano 等库。

我想了解 Vulkan API 的一个具体的、我想相当实用的细节。我知道 GPU 可能是一个相当复杂的话题,但我想强调的是,我没有任何低级图形或 Vulkan 的背景,所以我理解如果我的问题无法回答,或者我的问题甚至没有感觉。我会尽量使用正确的术语。我不得不承认,我不是最擅长浏览和查看大量我不太理解的源代码但仍然掌握整体概念的,这就是为什么我希望我能在这里找到我的答案。我曾尝试查看 Vulkan 和 Mesa 驱动程序的源代码,但没有结果。

原始问题:
我想了解 API 调用如何传播到 GPU 驱动程序。

我四处搜索,但找不到我要搜索的细节。我找到的最接近的帖子是这两个:
https://softwareengineering.stackexchange.com/questions/279069/how-does-a-program-talk-to-a-graphics-card
https://superuser.com/questions/461022/how-does-the-cpu-and-gpu-interact-in-displaying-computer-graphics

他们都提到了类似于“为了让 GPU 做某事,您必须通过支持的 API 进行调用”的内容。我知道这一点,但两者都没有深入研究 API 调用是如何进行的。希望下图说明了我的问题。

MyVulkanProgram.c with "#include <vulkan/vulkan.h>"
|
| (Makes call via Vulkan API)
v
This is the part I don't understand!
|
v
Driver (Mesa,for example) takes the request sent via the Vulkan API.
|
| (Driver asks GPU to perform task)
v
GPU does task

我不在乎 GPU 做什么或如何做某事。它是如何通过 Vulkan 通过 API 调用调用的,以及它是如何在系统中传播的。理想情况下,我正在寻找的是代码片段或链接到 Vulkan 源代码中实际请求发送到驱动程序的位置。

还是我弄错了? Vulkan 是否比我意识到的更成为驱动程序的一部分?驱动程序是否包含与我的“MyVulkanProgram.c”相同的 Vulkan 标头并且驱动程序与库文件(例如 libvulkan.so 等)链接在一起?是不是更像下图?

MyVulkanProgram.c with "#include <vulkan/vulkan.h>"
|
| (Makes call via Vulkan API)
v
Driver (Mesa,for example,which includes the vulkan headers and is linked with the Vulkan shared object-files) takes the request sent via the Vulkan API.
|
| (Driver asks GPU to perform task)
v
GPU does task

可能是一个基本问题,也可能不是,但我还是很困惑。非常感谢您的回答!

更新问题:
在阅读了@krOoze (answer from krOoze) 的回答,并给出了上述文档中的“Vulkan loader”概览图后,我可以更准确地表达我的问题。

通过 Vulkan API 进行调用的应用程序如何通过 Vulkan 加载器到达 ICD?

解决方法

您正在寻找 Vulkan-Loader/LoaderAndLayerInterface.md 文档。

该应用程序与加载程序(有时称为 Vulkan RT 或 Vulkan Runtime)交互。那就是 vulkan-1.dll(或 so)。

Loader 也有 vulkan-1.lib,这是经典的 dll shim。这是加载核心版本和 WSI 命令的地方,但您可以跳过 lib 并使用 dll 直接从 vkGetInstanceProcAddr 手动完成所有操作。

然后您就有了 ICD(可安装的客户端驱动程序)。这些类似于 nvoglv64.dll,您可以在您的 PC 上拥有更多它们(例如 Intel iGPU + NV)。该名称是任意的且特定于供应商。加载器通过配置文件找到它们。

现在,当您对使用 vkGetInstanceProcAddress 获得的命令调用某些内容时(如果您仅使用 *.lib,这就是一切),您将进入一个加载程序蹦床,它调用一系列层,然后调用相关的 ICD(或所有 ICD)。然后调用堆栈展开,因此它会向另一个方向移动,直到返回到应用程序。加载器互斥并将输入和输出合并到 ICD。

使用 vkGetDeviceProcAddress 获得的命令稍微简化了一点,因为它们不需要互斥或合并,并且旨在传递给 ICD,而无需加载器的太多干预。

代码也在同一个仓库:trampoline.cloader.c。这很简单;每一层都只是调用它下面的层。从蹦床开始,到终结者层结束,后者又称为 ICD 层。