关注专栏 写文章
第 8 章 管线以及管线状态管理

第 8 章 管线以及管线状态管理

9 人 赞同了该文章

第 8 章 管线以及管线状态管理

在上一章中,我们理解了 Vulkan 中的缓冲区资源,并使用它在物理设备内存上以顶点缓冲区的形式存储几何图形数据信息。 我们实现了一个 Render Pass 和 framebuffer 对象。 此外,我们知道了 SPIR-V,这是 Vulkan 中指定着色器的一种新方法。 此外,我们使用 SPIR-V 工具库在编译时将 GLSL 着色器转换为 SPIR-V 中间语言。

之前学到的内容先告一段落,在这一章中, 我们将理解管线和管线状态管理的概念。 在本章中,我们会介绍一下 Vulkan API 支持的管线类型。 有两种类型的管线:计算和图形。 这些管线是使用管线缓存对象创建的,这是下一个主题。 在本章结束时,我们将实现图形管线,并彻底了解与之相关的管线状态的各种类型。.

在本章中,我们将涵盖以下主题:

  • 管线入门
  • 使用管线缓存对象 pipeline cache object(PCO)缓存管线对象
  • 创建图形管线
  • 了解计算管线
  • Vulkan 中的管线状态对象
  • 实现管线

管线入门

管线是指数据输入流经的一系列固定阶段;每个阶段都处理传入的数据并将其传递到下一个阶段。 最终产品或是一个 2D 栅格绘图图像(图形管线),或是 使用计算逻辑和计算操作(计算管线)更新后的资源(缓冲区或图像)。

Vulkan 支持两种类型的管线,即图形和计算。

  • 图形管线:graphics pipeline,该管线通过命令缓冲区接收若干 Vulkan 命令并绘制 2D / 3D 场景的 2D 光栅化图像。
  • 计算管线:compute pipeline,该管线通过命令缓冲区接收 Vulkan 命令并处理它们以进行相关的计算工作。

如下的图示(来自 Vulkan 官方规范(khronos.org/registry/vu diagram)显示了 Vulkan 图形管线和计算管线:

<img src="https://pic3.zhimg.com/v2-83dd888c32c0e5022352d0f0da96f97e_b.jpg" data-caption="" data-size="normal" data-rawwidth="1192" data-rawheight="1096" class="origin_image zh-lightbox-thumb" width="1192" data-original="https://pic3.zhimg.com/v2-83dd888c32c0e5022352d0f0da96f97e_r.jpg"/>


管线行程从 Input Assembler开始,输入的顶点数据根据指定的基本拓扑结构以点、线和三角形的形式进行组装。 使用顶点着色器 Vertex Shader可编程阶段,输入的顶点数据被转换到一个剪辑空间。 几何图形在Tessellation Control ShaderTessellation Evaluation Shader装配器中进行细分。 几何图形着色器 Geometry Shader 具有从单个传入图元中生成多个图元的独特功能。

接下来,Primitive Assembler从前一阶段获取转换过的所有坐标,并按照输入阶段提供的指定绘图操作或原始类型(点,线和三角形)信息有序地对它们进行排列。 当关联的顶点坐标落在视景体之外时,图元就会被裁剪掉,当发生这种情况时,裁剪掉的片段(视野之外)会被丢弃。

栅格化 Rasterization 是将变换后的屏幕空间图元(点,线和三角形)转换为称之为片段的离散元素的过程。 这些片段由下一个阶段控制,称为片段着色器 Fragment Shader。 片段着色器在单个片段上执行计算。 这些片段最终会成为帧缓冲区的一部分,这个帧缓冲区全部经历了大量条件的更新,例如深度测试,模版测试和片段混合。

缓冲区和图像内存类型可以以 1D / 2D / 3D 工作组的形式 (称为计算管线)在一个单独的管线中进行处理。 计算管线在并行处理过程中完成工作的能力是非常强大的;它主要用于图像处理和物理计算领域。 计算管线可以修改(读取 / 写入)缓冲区和图像内存。

该管线大致由三个概念组成:管线状态对象,管线缓存对象和管线布局。 这些可用于有效控制底层管线的操作:

  • 管线状态对象(PSO):Pipeline state objects (PSO),物理设备或 GPU 能够直接在硬件中执行多种操作。 这些操作可能包括光栅化器和条件更新,例如混合深度测试,模版测试等。 Vulkan 在 PSO 的帮助下提供了控制这些硬件设置的能力。 其他基于硬件的操作可能包括:在给定的几何形状上组装基本拓扑类型(点 / 线 / 三角形),视口控制等。
  • 管线缓存对象(PCO):Pipeline cache objects (PCOs),管线缓存提供了更快地检索和重用存储过的管线的一种机制。 这为应用程序避免创建类似或重复的冗余管线对象提供了一个更好机会。
  • 管线布局:Pipeline layouts,缓冲区和图像被间接连接到着色器,可以使用着色器资源变量对其进行访问。 资源变量被连接到缓冲区视图和图像视图, 这些资源变量通过描述符和描述符集布局进行管理。 在管线内,管线布局管理一系列的描述符集布局。

注意

描述符是存储的资源和着色器阶段之间的接口。 资源连接到由描述符集布局定义的逻辑布局绑定,并且管线布局提供对管线内描述符集的访问。 描述符会在第 10 章“描述符和 Push 常量”中详细介绍,我们还会在这里学习使用 uniform。

VulkanPipeline- 管线实现类

在本章中,我们将介绍一个名为 VulkanPipeline 的用户定义类。 该类会管理 Vulkan 应用程序的管线实现。 由于这个类要处理大量的管线状态管理对象,因此管线创建是性能关键点;因此,管线对象的可重用性是非常重要的。 Vulkan 管线通过 PCO 提供管线缓存机制。 这减少了创建类似管线的开销 - 驱动程序会寻找比较接近的匹配,并使用基本管线创建新管线。

管线的实现必须放置在绘制对象可以轻松访问 PCO 的集中位置,以提高管线可重用性。 为此,有两种选择:将 VulkanPipeline 类放置在 VulkanApplication 内(在应用程序级别,这可能是主线程)或 VulkanRenderer 内(用于每个独立的渲染线程)。 只要应用程序能够通过正确处理内存泄漏和线程同步合理地管理管线对象,那么这两个选项使用哪个都可以。 本书遵循后一个选项,因此我们将 VulkanPipeline 放置在 VulkanRenderer 中以避免线程的同步,使其对初学者来说更简单一些。

以下框图显示了一个图形视图,该视图表示应用程序系统与用户定义的 VulkanPipeline 类的集成:

<img src="https://pic2.zhimg.com/v2-3cafa5ac2d340f07e410fe5c8441f815_b.jpg" data-caption="" data-size="normal" data-rawwidth="929" data-rawheight="429" class="origin_image zh-lightbox-thumb" width="929" data-original="https://pic2.zhimg.com/v2-3cafa5ac2d340f07e410fe5c8441f815_r.jpg"/>


以下是 VulkanPipeline 头文件(VulkanPipeline.h)的声明:

/************ VulkanPipeline.h ************/
class VulkanPipeline
{
public:
// Creates the pipeline cache object and stores pipeline object
void createPipelineCache();