1、实例和物理设备选择
Vulkan应用程序首先通过VkInstance(VkInstance instance;),创建实例后可查询支持的硬件,可以选择一个或者多个VkPhysicalDevice(VkPhysicalDevice physicalDevice = VK_NULL_HANDLE).
2、逻辑设备和队列系列
选择要使用的正确硬件设备后,创建一个VkDevice(VkDevice device),还需要指定要使用的队列系列。大多数使用 Vulkan 执行的操作,如绘制命令和内存操作,都是通过将它们提交给一个异步执行的VkQueue(VkQueue graphicsQueue;).
3、 窗口表面和交换链
需要创建一个窗口来呈现渲染图像。需要2个组件来实际渲染到一个窗口:①VkSurfaceKHR(VkSurfaceKHR surface;),②VkSwapchainKHR(VkSwapchainKHR swapChain;)。KHR后缀意味着这些对象是Vulkan扩展的一部分。
Surface 是对要渲染到的窗口的跨平台抽象,通常通过提供对本机窗口句柄的引用来实例化,例如HWND
在 Windows 上。
交换链是渲染目标的集合。它的基本目的是确保我们当前渲染的图像与当前屏幕上的图像不同。这对于确保只显示完整图像很重要。每次我们想要绘制一个帧时,我们都必须要求交换链为我们提供要渲染的图像。当我们完成绘制帧时,图像将返回到交换链,以便在某个时候显示在屏幕上。渲染目标的数量和将完成的图像呈现到屏幕上的条件取决于当前模式。
4、图像视图和帧缓冲区
要绘制从交换链获取的图像,我们必须将其包装到一个 VkImageView(std::vector swapChainImageViews;)和VkFramebuffer(std::vector swapChainFramebuffers;)中。
5、渲染通道
仅描述图像类型。
6、图形流水线
Vulkan 中的图形管道是通过创建一个VkPipeline
(VkPipeline graphicsPipeline;)对象来设置的。它描述了图形卡的可配置状态,如视口大小和深度缓冲区操作以及使用VkShaderModule
(VkShaderModule shaderModule;)对象的可编程状态。这些VkShaderModule
对象是从着色器字节代码创建的。驱动程序还需要知道管道中将使用哪些渲染目标,我们通过引用渲染通道来指定。
7、命令池和命令缓冲区
如前所述,我们要执行的 Vulkan 中的许多操作(例如绘图操作)需要提交到队列。这些操作首先需要记录到aVkCommandBuffer
(std::vector commandBuffers;)中才能提交。这些命令缓冲区是从VkCommandPool
(VkCommandPool commandPool;)与特定队列系列关联的队列中分配的。要绘制一个简单的三角形,我们需要使用以下操作记录一个命令缓冲区:
因为帧缓冲区中的图像取决于交换链将给我们的特定图像,所以我们需要为每个可能的图像记录一个命令缓冲区,并在绘制时选择正确的图像。
8、主循环
我们首先使用 获取交换链中的图像vkAcquireNextImageKHR
。(VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);)解释如下:
device:Vulkan逻辑设备
swapChain:Vulkan交换链
UINT64_MAX:超时时间,表示不超时。
imageAvailableSemaphores[currentFrame]:信号量,用于同步交换链中的图像。
VK_NULL_HANDLE:没有可用的图像。
&imageIndex:指向整数类型的指针,用于存储获取到的图像索引。
然后我们可以为该图像选择适当的命令缓冲区并使用vkQueueSubmit
. (if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error(“failed to submit draw command buffer!”);
})
最后,我们将图像返回到交换链以显示到屏幕上vkQueuePresentKHR
。(result = vkQueuePresentKHR(presentQueue, &presentInfo)