Vulkan 初始化
与 OpenGL 允许您几乎立即执行图形命令不同,Vulkan 有一个漫长的设置阶段。为了缩短这个阶段,我们将使用库 VkBootstrap
,它在处理所有这些样板代码方面提供了很大帮助。
由于 Vulkan 是一个非常明确的 API,它提供了非常“直接”的控制,因此您需要对其进行初始化,以执行诸如加载扩展、选择您将要使用的 GPU(或多个!)以及创建初始 VkInstance
和 VkDevice
结构等操作,然后您可以使用这些结构来执行 Vulkan 命令。
与 OpenGL 不同,Vulkan 没有全局状态,因此您需要在每次 API 函数调用时都发送 VkDevice
或 VkInstance
。在本指南中,我们将直接链接到 vulkan-1.dll
以获取函数,但这并非必要。您可以“手动”加载函数指针。链接到 vulkan-1.dll
也不会加载扩展函数,您需要自行加载这些函数。
VkInstance
一切的根源是 VkInstance
。它代表 Vulkan API 上下文。在创建 VkInstance
时,您可以根据需要启用验证层、您需要的实例扩展(如 VK_KHR_surface),还可以连接您选择的记录器,以便在 Vulkan 驱动程序出现错误或需要记录某些内容时使用。实例创建期间的主要任务是启用验证层和实例扩展。
一般来说,应用程序在其整个运行过程中只需要创建一个 VkInstance
,因为它只是应用程序的全局 Vulkan 上下文。
VkPhysicalDevice
一旦我们创建了 VkInstance
,我们就可以查询系统中可用的 GPU。
Vulkan 允许我们获取系统中 GPU 的列表及其功能。所有这些信息都表示在 VkPhysicalDevice
句柄上,它是对“实际” GPU 的引用。例如,在专用游戏 PC 中,可能只有一个 VkPhysicalDevice
可用,即专用游戏 GPU。在这种情况下,无需在 GPU 之间进行选择,因为只有一个 GPU。
更有趣的情况是在笔记本电脑等设备中。笔记本电脑通常有 2 个 GPU,一个是 CPU 集成的 GPU(低功耗),另一个是专用 GPU(高功耗)。在这种情况下,应用程序需要决定使用哪个 GPU 进行渲染,并且最好将选择权留给用户,以防用户可能想要使用功能较弱的专用 GPU 来延长电池续航时间。
除了选择要使用的 GPU 之外,VkPhysicalDevice
还允许我们查询其具有的功能、GPU 的内存大小或可用的扩展。这对于高级应用程序非常重要,在这些应用程序中,您想确切地知道您有多少 VRAM 可用,以及 GPU 是否支持高级功能。
VkDevice
一旦我们有了要使用的 GPU 的 VkPhysicalDevice
,我们就可以从中创建一个 VkDevice
。这是 GPU 硬件上的实际 GPU 驱动程序,也是我们与所述 GPU 通信的方式。大多数 Vulkan 命令(调试实用程序或初始化内容除外)都需要 VkDevice
。设备是使用您要启用的扩展列表创建的。强烈建议您不要启用不需要的扩展,因为它们可能会因检查额外内容而导致驱动程序速度变慢。
Vulkan 在创建时的最重要目标之一是,可以“手动”完成多 GPU。这是通过为您要使用的每个 GPU 创建一个 VkDevice 来完成的,然后可以在 VkDevice 之间共享数据。一个可能的方案是在您的主专用 GPU 上创建一个 VkDevice 用于实际图形,但保留一个 VkDevice 用于集成 GPU 来运行一些物理计算或其他数据。
交换链
初始化 GPU 很好,但我们实际上想要执行一些渲染到屏幕上。这就是交换链的用武之地。交换链不在核心 Vulkan 规范中,因为它们是可选的,并且通常对于不同的平台是唯一的。如果您以无头模式运行 Vulkan,例如渲染图像并将其保存到磁盘,或执行离线计算,则不需要创建交换链,因为交换链仅用于将图像发送到屏幕。
交换链是按给定大小创建的,如果窗口大小调整,您将必须再次重新创建交换链。
交换链为其图像公开的格式在平台甚至 GPU 之间可能有所不同,因此您必须存储交换链所需的图像格式,因为以不同的格式渲染会导致伪影或崩溃。
交换链是图像列表,操作系统可以访问这些图像以显示到屏幕。您可以创建具有更多或更少图像的交换链,但通常您只需要 2 或 3 个图像来执行双缓冲或三缓冲渲染。
创建交换链时最重要的事情是选择呈现模式,这就是交换链在逻辑上“工作”的方式。
- VK_PRESENT_MODE_IMMEDIATE_KHR 使交换链无需等待任何内容,并接受即时推送图像。这可能会导致撕裂,通常不建议使用。
- VK_PRESENT_MODE_FIFO_KHR 这将有一个图像队列,用于在刷新间隔时呈现。一旦队列已满,应用程序将必须等待直到通过显示图像来弹出队列。这是“强 VSync”呈现模式,它会将您的应用程序锁定到屏幕的 FPS。
- VK_PRESENT_MODE_FIFO_RELAXED_KHR。与 Fifo VSync 大致相同,但 VSync 是自适应的。如果您的应用程序的 FPS 低于屏幕的最佳 FPS,它将立即推送图像,这可能会导致撕裂。例如,如果您的屏幕是 60 HZ 屏幕,并且您以 55 HZ 渲染,则它不会下降到下一个 vsync 间隔,从而使您的总体 FPS 像 Fifo 那样下降到 30,而是它仍然会以 55 FPS 显示图像,但会出现撕裂。
- VK_PRESENT_MODE_MAILBOX_KHR。这个模式有一个图像列表,当其中一个图像正在被屏幕显示时,您将不断地渲染列表中的其他图像。每当需要显示图像时,它都会选择最新的图像。如果您想要没有硬垂直同步的三缓冲,则可以使用此模式。
VK_PRESENT_MODE_IMMEDIATE_KHR 由于其撕裂现象而很少使用。仅在极低延迟的情况下,允许撕裂现象可能有用。
正常应用程序将使用 MAILBOX 或 2 种 FIFO 模式之一。主要取决于您是否想要硬垂直同步或更喜欢三缓冲。
在本指南中,我们将使用 FIFO_RELAXED 模式,因为它对我们的渲染速度实施了上限,并且由于我们不打算渲染太多对象,因此最好限制帧速率,而不是达到可能导致问题的 5000 FPS。在需要完成一些实际工作的真实应用程序中,默认情况下首选 MAILBOX 模式。
下一步: 初始化 vulkan 代码