美文网首页
Vulkan介绍

Vulkan介绍

作者: Dragon_boy | 来源:发表于2020-09-09 10:57 被阅读0次

    https://vulkan-tutorial.com/

    Vulkan是Khronos group开发的一个新的API,对于现代显卡有着更好的抽象。Vulkan背后的设计理念类似于DX12和Metal,但Vulkan是跨平台的,这就是一个极大的优势。

    绘制一个三角形的流程

    这里简要的介绍一下Vulkan绘制一个三角形的过程以及用到的一些概念。

    实例和物理设备的选择

    一个Vulkan应用程序首先通过VkInstance初始化,一个实例被创建来描述你的应用程序,可以使用任何API的扩展。在创建实例后,我们可以对Vulkan支持的硬件设备进行查询,选择一个或多个VkPhsicalDevice用来进行操作。我们可以根据虚拟内存大小或者设备的能力来选择目标设备。

    逻辑设备和队列族

    在选择正确的硬件设备后,我们需要创建一个VkDevice(逻辑设备),其中会更精确地描述我们会使用的VkPhsicalDeviceFeatures,如多重视窗渲染和64位浮点值。我们还需要确定要使用哪种队列族。Vulkan执行的大多数操作,类似于绘制命令和内存操作,它们是送往VkQueue然后异步执行的。队列指派自队列族,其中每个队列族在它的队列中提供一些特殊的操作集。例如,对于图形、计算和内存转移操作可以各自有单独的队列族。队列族的可用性可以用来评定物理设备选择这一操作。可能支持Vlulkan的设备不提供所有图形功能,当然,目前基本都提供。

    窗口表面和交换链

    窗口可以通过本地平台API或库来创建,如GLFW。

    我们需要两个组件来将图像渲染到一个窗口:一个窗口表面(VkSurfaceKHR)和一个交换链(VkSwapchainKHR)。注意KHR后缀,这意味着这些对象是Vulkan扩展的一部分。Vulkan API虽说是跨平台的,但还是要用对应平台的相应接口扩展来管理窗口。表面是抽象自窗口要渲染到的目标的一个概念,通常对窗口句柄提供一个引用来初始化,例如Windows上的HWND,幸运的是,GLFW库有类似的内置方法来处理平台特性。

    交换链是渲染目标集,它的最基本的目的是确保我们当前渲染的图片与当前显示在屏幕上的不一样。确保只有完整的图片显示在屏幕上是非常重要的。每次我们想要绘制一帧时,我们必须让交换链提供一张要渲染的图片。当我们绘制完一帧时,图片会返回到交换链,然后在某些情况下显示在屏幕上。渲染目标和显示完成图片到屏幕的条件取决于当前的模式。通常的显示模式时双缓冲和三缓冲。

    一些平台允许你不与任何窗口管理器交互就可以直接渲染到显示器上,通过VK_KHR_displayVK_KHR_display_swapchain扩展。这允许你创建一个可以表示整个屏幕的表面,可以用来实现自己的窗口管理器。

    图片视图和帧缓冲

    为了绘制一张从交换链得来的图片,我们需要包裹到一个VkImageViewVkFramebuffer。一个图片视图引用一张使用图片的特殊部分,一个帧缓冲索引用于颜色、深度和模板目标的图片视图。因为交换链中会有很多不同的图片,我们可以提前为每张图片创建图片试图和帧缓冲,并在绘制时选择正确的一张。

    渲染过程

    在Vulkan中渲染过程描述为在渲染操作时使用的图片的类型,它们如何被使用以及它们的环境应该如何被对待。在我们的一开始的三角形渲染应用中,我们会告知Vulkan我们会将单张图片作为颜色目标使用,我们想让在渲染操作前清空为一个单一颜色。无论一个渲染过程只是描述图片的类型,一个VkFramebuffer实际上便规定特定的图像到这些语义中。

    图形管线

    Vulkan中的图形管线通过创建VkPipeline对象初始化,它描述了显卡的配置状态,如视口大小、深度缓冲操作和使用VkSshaderModule对象的可编程阶段。VkShaderModule对象由着色器字节码创建。驱动也需要知道在管线中使用的渲染目标时什么,需要引用哪个特定的渲染过程。

    Vulkan和其它API的最大的区别是几乎所有的图形管线配置都需要提前设置,这意味着如果我们需要切换到不同的着色器或简单地改变顶点布局,然后我们需要完全重新创建图形管线。这意味着我们需要对所有我们需要的渲染操作的不同集合提前创建许多的VkPipeline对象。只有一些基本的配置,就像视口大小和清除颜色,可以动态改变。所有的状态也需要精确描述,例如没有默认的颜色混合状态。

    命令池和命令缓冲

    在之前提到过,在Vulkan中我们想要执行的许多操作,如绘制操作,需要提交到一个队列中。这些操作在被提交前首先需要被收录到一个VkCommandBuffer中。这些命令缓冲从与一个特定的队列族绑定的VkCommandPool得到。为了绘制一个简单的三角形,我们需要使用下列的操作来记录一个命令缓冲:

    • 开始渲染过程
    • 绑定图形管线
    • 绘制三个顶点
    • 结束渲染过程

    因为在帧缓冲中的图片取决于交换链会给我们哪一张特定的图片,我们需要对每个可能的图片记录一个命令缓冲,并在绘制时选择一个正确的图片。

    主循环

    现在,绘制命令被包裹在一个命令缓冲中,主循环的实现就一目了然了。我们首先使用vkAcquireNextImageKHR来从交换链中获得一个图片。我们然后可以对该涨图片选择一个合适的命令缓冲,使用vkQueueSubmit执行。最后,我们返回一张图片到交换链中,使用vkQueuePresentKHR显示到屏幕上。

    提交到队列中的操作是异步执行的,因此我们需要使用如信号灯的同步物体来确保正确的执行顺序。绘制命令缓冲的执行必须初始化,等待图片获取的完成,否则会在开始渲染到一张图片时仍在为显示屏幕上到进行读取操作。vkQueuePresentKHR按顺序调用,需要等待渲染完成,对此我们会使用一个秒信号,在渲染结束后会信号指示。

    总结

    简单总结一下,绘制一个简单的三角形需要:

    • 创建一个VkInstance
    • 选择支持的显卡(VkPhysicalDevice)
    • 针对绘制和显示创建一个VkDeviceVkQueue
    • 创建一个窗口、窗口表面和jiaohuanlian
    • 将交换链图片包裹到VkImageView
    • 创建一个描述一个渲染目标和使用的渲染过程
    • 为渲染过程创建一个帧缓冲
    • 初始化渲染管线
    • 声明和记录一个命令缓冲,使用每张可能的交换链图片的绘制命令
    • 使用要求的图片绘制帧,提交正确的绘制命令缓冲,返回图片到交换链

    API概念

    这里简单的介绍一下API实现

    语法

    Vulkan的函数、枚举和结构体定义在vulkan.h头文件。

    函数使用vk前缀,枚举和结构体等有Vk前缀,枚举值有Vk_前缀。举个例子:

    VkXXXCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_XXX_CREATE_INFO;
    createInfo.pNext = nullptr;
    createInfo.foo = ...;
    createInfo.bar = ...;
    
    VkXXX object;
    if (vkCreateXXX(&createInfo, nullptr, &object) != VK_SUCCESS){
        std::cerr << "failed to create object" << std::endl;
        return false;
    }
    

    相关文章

      网友评论

          本文标题:Vulkan介绍

          本文链接:https://www.haomeiwen.com/subject/urfjektx.html