19Vulkan——Pipeline管线

1.管线

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

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

  • 图形管线:graphics pipeline,该管线通过命令缓冲区接收若干 Vulkan 命令并绘制 2D / 3D 场景的 2D 光栅化图像。
  • 计算管线:compute pipeline,该管线通过命令缓冲区接收 Vulkan 命令并处理它们以进行相关的计算工作。
  1. 管线行程Input Assembler开始,输入的顶点数据根据指定的基本拓扑结构以点、线和三角形的形式进行组装。 使用顶点着色器 Vertex Shader可编程阶段,输入的顶点数据被转换到一个剪辑空间。 几何图形在Tessellation Control ShaderTessellation Evaluation Shader装配器中进行细分。 几何图形着色器 Geometry Shader 具有从单个传入图元中生成多个图元的独特功能。
  2. 接下来,Primitive Assembler从前一阶段获取转换过的所有座标,并按照输入阶段提供的指定绘图操作或原始类型(点,线和三角形)信息有序地对它们进行排列。 当关联的顶点座标落在视景体之外时,图元就会被裁剪掉,当发生这种情况时,裁剪掉的片段(视野之外)会被丢弃。
  3. 栅格化 Rasterization 是将变换后的屏幕空间图元(点,线和三角形)转换为称之为片段的离散元素的过程。 这些片段由下一个阶段控制,称为片段着色器 Fragment Shader片段着色器在单个片段上执行计算。 这些片段最终会成为帧缓冲区的一部分,这个帧缓冲区全部经历了大量条件的更新,例如深度测试,模版测试和片段混合。
  4. 缓冲区和图像内存类型可以以 1D / 2D / 3D 工作组的形式 (称为计算管线)在一个单独的管线中进行处理。 计算管线在并行处理过程中完成工作的能力是非常强大的;它主要用于图像处理和物理计算领域。 计算管线可以修改(读取 / 写入)缓冲区和图像内存。

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

管线状态对象(PSO):Pipeline state objects (PSO),物理设备或 GPU 能够直接在硬件中执行多种操作。 这些操作可能包括光栅化器和条件更新,例如混合深度测试,模版测试等。 Vulkan 在 PSO 的帮助下提供了控制这些硬件设置的能力。 其他基于硬件的操作可能包括:在给定的几何形状上组装基本拓扑类型(点 / 线 / 三角形),视口控制等。

管线缓存对象(PCO):Pipeline cache objects (PCOs),管线缓存提供了更快地检索和重用存储过的管线的一种机制。 这为应用程序避免创建类似或重复的冗余管线对象提供了一个更好机会。

管线布局:Pipeline layouts,缓冲区和图像被间接连接到着色器,可以使用着色器资源变量对其进行访问。 资源变量被连接到缓冲区视图和图像视图, 这些资源变量通过描述符和描述符集布局进行管理。 在管线内,管线布局管理一系列的描述符集布局。

2.pipeline的实现

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

该视图表示应用程序系统与用户定义的 VulkanPipeline 类的集成:

使用 PCO 缓存管线对象 pipeline objects

管线缓存是一个用于存储管线的池, 它使应用程序能够减少管线运行之间以及后续应用程序运行之间的管线创建开销。 以下是两者之间的区别:

  • 管线 pipelines 之间:当新的管线被创建时,管线构造是可以重用的。 管线缓存对象作为参数传递给管线创建器 API(vkCreateGraphicsPipelines)。 通过这样做,底层的机制可确保在存在类似的管线时能够对其进行重用。 这在创建本质上是冗余的绘图对象时非常有用,例如绘画画笔,精灵,网格几何体等等。
  • 在应用程序 applications 之间:在创建管线时,会处理大量的管线状态对象,这是一种昂贵的操作。 在正在运行的应用程序中重用是一个明智的设计,并且在执行时间和内存空间方面非常高效。 Vulkan 应用程序中的管线缓存可以通过序列化管线缓存对象来有效地进行重用。 应用程序从序列化的管线缓存中检索存储的管线对象并对其进行预初始化。 在后续运行中,可以在多个应用程序运行中重复使用相同的序列化 PCO。

2.1创建管线缓存对象 pipeline cache object

PCO 可用于创建图形管线(vkCreateGraphicsPipelines)或计算管线(vkCreateComputePipelines)。 使用 PCO 创建这些管线可确保管线对象的可重用性。 如果 PCO 不包含类似的管线,则会创建一个新的管线并将其添加到它的池中。

可以使用 vkCreatePipelineCache()API 创建 PCO。 在成功创建后,会返回一个 VkPipelineCache 对象。

VkResult vkCreatePipelineCache(
VkDevice    device,
const VkPipelineCacheCreateInfo*    pCreateInfo, 
const VkAllocationCallbacks*   pAllocator, 
VkPipelineCache*        pPipelineCache);

 2.2从管线缓存 pipeline caches 检索数据

管线缓存对象可以以字节流的形式保存其信息。 当应用程序重新运行或再次执行时,这些存储的信息可以在以后重用。 管线缓存数据可以使用 vkGetPipelineCacheData()API 检索:

VkResult vkGetPipelineCacheData(
VkDevice    device,
VkPipelineCache pipelineCache,
size_t* dataSize,
void*   data);

 

3.创建图形管线 graphics pipeline

图形管线由可编程的固定功能管线阶段渲染通道子通道以及管线布局组成。 可编程阶段包括多个着色器阶段,例如顶点着色器、片段着色器、表面细分着色器、几何着色器和计算着色器。 固定功能的状态由多个管线状态对象 pipeline state objects (PSO)组成,这些对象表示动态的信息:顶点输入,输入的装配,光栅化,混合,视口,多重采样和深度模板状态。

使用 vkCreateGraphicsPipelines()API 创建图形管线对象(VkPipeline)。 该 API 会通过名为 VkGraphicsPipelineCreateInfo 的元数据控制结构影响可编程阶段,固定功能管线阶段和管线布局。

3.1.实现图形管线 graphics pipeline

图形管线在 VulkanPipeline 的 createPipeline()函数中实现。 该函数需要四个参数。 第一个参数包含顶点输入和数据解析。 第二个参数包含返回值,会在其中创建管线数组。 第三个参数是一个表示深度测试的布尔标志。 最后一个参数用于指定是否考虑顶点输入。

图形管线由几个管线状态对象,渲染通道,着色器对象和子通道组成,如下图所示。 通过创建 VkGraphicsPipelineCreateInfo 对象(pipelineInfo)并在其中指定所有的各种状态对象,着色器对象以及渲染通道对象来完成管线的实现。 最后,vkCreateGraphicsPipelines()API 使用管线对象创建管线。 下图显示了图形管线及其所有的接口控制器:

4.计算管线 compute pipelines

计算管线由一个静态的计算着色器阶段管线布局组成。 计算着色器阶段能够任意进行大规模并行计算。 另一方面,管线布局使用布局绑定将计算管线连接到描述符。 vkCreateComputePipeline()API 可用于创建计算管线:

VkResult vkCreateComputePipelines(
VkDevice  device,
VkPipelineCache pipelineCache,
uint32_t  createInfoCount,
const VkComputePipelineCreateInfo*  pCreateInfos, 
const VkAllocationCallbacks*    pAllocator, 
VkPipeline*     pPipelines);

5.Vulkan 中的管线状态对象 Pipeline State Objects-PSO

管线中的管线状态对象是控制物理设备硬件设置的一种手段。 管线中指定了各种类型的管线状态对象;以预定义的顺序进行工作。 这些阶段中的输入数据和资源会根据用户指定的行为进行更改。 每个阶段都会处理输入并将其传递给下一个输入。 根据应用程序的要求,管线状态阶段可以根据用户的选择绕过。 这完全可以通过 VkComputePipelineCreateInfo 进行配置。

在我们详细介绍这些管线状态对象之前,先让我们对它们进行一个简单的概述:

  • 动态状态:dynamic state, 它指定了在此管线中使用的动态状态。
  • 顶点输入状态:vertex input state, 这指定了数据输入速率及其解释。
  • 输入装配状态:input assembly state, 这个把顶点数据组装成图元的拓扑(线,点和三角形变体)。
  • 光栅化状态:rasterization state, 此操作与光栅化相关,如多边形填充模式,正面信息,剔除模式信息等。
  • 颜色混合状态:color blend state, 这指定源片段和目标片段之间的混合因子和操作。
  • 视口状态:viewport state, 它定义了视口,裁剪器和尺寸。
  • 深度 / 模板状态:depth/stencil state, 这指定了如何执行深度 / 模板操作。
  • 多重采样状态:multisampling state, 这个控制着栅格化期间用于抗锯齿目的的像素描绘中使用的采样。

以下图表说明了 PSO 中的每个阶段:

光栅化 Rasterization

片段是在光栅化阶段使用基本的拓扑结构生成的。 光栅化的图像由称为片段的小方块组成,以网格方式排列。片段是帧缓冲区中(x,y)位置的逻辑组合,对应于深度(z)以及片段着色器添加的相关数据和属性。

每个图元都要经过光栅化过程,并根据拓扑形状确定相应的片段。 在此,计算每个图元的整数位置,从而产生帧缓冲区网格上对应的点或方块。 与位置一起,负责确定最终片段颜色的一个或多个深度值及其属性会被存储在帧缓冲区中。 每个计算点可以有一个或多个深度值;这表明存在多个重叠的图元(完全或部分)竞争相同的位置。 然后根据深度和相关属性信息解析这些片段。

片段可能是非方形的,这并不影响光栅化过程的工作方式。 栅格化与片段的高宽比无关。 由于正方形简化了抗锯齿和纹理化的过程,因此将片段假定为正方形。

最终计算出的片段是一个对应于帧缓冲区的像素。 任何属于帧缓冲区维度以外的片段都会被丢弃,并且在管线后期阶段不再考虑,包括任何早期的逐片段测试。 片段着色器处理幸存的片段,并修改现有的深度值以及与片段相关联的数据。

多重采样

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章