Ch3-3 管线布局和管线

Pipeline Layout

管线布局(VkPipelineLayout)包含了管线如何使用描述符以及push constant的信息。

创建管线布局

vkCreatePipelineLayout(...)创建管线布局:

VkResult VKAPI_CALL vkCreatePipelineLayout(...) 的参数说明

VkDevice device

逻辑设备的handle

const VkPipelineLayoutCreateInfo* pCreateInfo

指向VkPipelineLayout的创建信息

const VkAllocationCallbacks* pAllocator

VkPipelineLayout* pPipelineLayout

若创建成功,将帧缓冲的handle写入*pPipelineLayout

struct VkPipelineLayoutCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineLayoutCreateFlags flags

uint32_t setLayoutCount

描述符集布局的数量

const VkDescriptorSetLayout* pSetLayouts

指向VkDescriptorSetLayout的数组,用于指定描述符集布局

uint32_t pushConstantRangeCount

Push constant范围的数量

const VkPushConstantRange* pPushConstantRanges

指向VkPushConstantRange的数组,用于指定push constant范围

struct VkPushConstantRange 的成员说明

VkShaderStageFlags stageFlags

会访问本结构体指定的push constant范围的阶段

uint32_t offset

在stageFlags所指定阶段中访问的push constant范围的起始位置

uint32_t size

在stageFlags所指定阶段中访问的push constant数据的总大小

封装为pipelineLayout类

VKBase.h,vulkan命名空间中添加以下代码:

class pipelineLayout {
    VkPipelineLayout handle = VK_NULL_HANDLE;
public:
    pipelineLayout() = default;
    pipelineLayout(VkPipelineLayoutCreateInfo& createInfo) {
        Create(createInfo);
    }
    pipelineLayout(pipelineLayout&& other) noexcept { MoveHandle; }
    ~pipelineLayout() { DestroyHandleBy(vkDestroyPipelineLayout); }
    //Getter
    DefineHandleTypeOperator;
    DefineAddressFunction;
    //Non-const Function
    result_t Create(VkPipelineLayoutCreateInfo& createInfo) {
        createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
        VkResult result = vkCreatePipelineLayout(graphicsBase::Base().Device(), &createInfo, nullptr, &handle);
        if (result)
            outStream << std::format("[ pipelineLayout ] ERROR\nFailed to create a pipeline layout!\nError code: {}\n", int32_t(result));
        return result;
    }
};

Pipeline

管线(VkPipeline)是数据处理流程的抽象,它由可编程着色器阶段、固定管线阶段、以及一系列状态参数构成。

创建图形管线

vkCreateGraphicsPipelines(...)创建图形管线:

VkResult VKAPI_CALL vkCreateGraphicsPipelines(...) 的参数说明

VkDevice device

逻辑设备的handle

VkPipelineCache pipelineCache

若非VK_NULL_HANDLE,则提供管线的缓存信息

uint32_t createInfoCount

图形管线的创建信息的个数

const VkGraphicsPipelineCreateInfo* pCreateInfos

指向VkGraphicsPipelineCreateInfo的数组,提供一个或多个管线的创建信息

const VkAllocationCallbacks* pAllocator

VkPipeline* pPipelines

若执行成功,将图形管线的handle写入pPipelines所指数组

VkPipelineCache类型的handle提供管线的缓存信息,其内容是实现特定的,换言之,VkPipelineCache类型是为显卡驱动创建管线所可能用到的自定义信息提供的接口。
利用上一次执行程序时的管线缓存信息可能可以加快对管线的创建,这么做可以加快程序的启动速度,尽管程序初始化的时间大都不是花在创建管线上。也可以在本次运行中,利用首次创建某条管线时的信息来加快重建管线,就这种情况而言,你也可以从旧管线衍生出新管线。
我个人认为VkPipelineCache的作用比较鸡肋,本套教程中不作具体讲解,如果你有兴趣使用,请自行阅读官方标准,这里我简单提要:

  • 创建一个没有初始数据的VkPipelineCache,在创建管线时传入其handle,Vulkan的实现会向其写入管线的缓存信息。

  • 如果想在下一次启动时加快创建管线的速度,那么将缓存信息存到文件,在下次启动时读取。

  • 缓存信息的头部信息(前32位)用于验证管线缓存是否满足显卡驱动的要求,以应对多显卡PC。

VkGraphicsPipelineCreateInfo

VkGraphicsPipelineCreateInfo内容繁多,专门用这一段对其进行说明。

struct VkGraphicsPipelineCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineCreateFlags flags

uint32_t stageCount

可编程管线阶段的数量

const VkPipelineShaderStageCreateInfo* pStages

指向VkPipelineShaderStageCreateInfo的数组,提供可编程管线阶段的创建信息,具体说明见管线着色器阶段的创建信息

const VkPipelineVertexInputStateCreateInfo* pVertexInputState

指向顶点输入状态的创建信息

const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState

指向输入装配状态的创建信息

const VkPipelineTessellationStateCreateInfo* pTessellationState

指向细分状态的创建信息

const VkPipelineViewportStateCreateInfo* pViewportState

指向视口状态的创建信息

const VkPipelineRasterizationStateCreateInfo* pRasterizationState

指向栅格化状态的创建信息

const VkPipelineMultisampleStateCreateInfo* pMultisampleState

指向多重采样状态的创建信息

const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState

指向深度模板状态的创建信息

const VkPipelineColorBlendStateCreateInfo* pColorBlendState

指向混色状态的创建信息

const VkPipelineDynamicStateCreateInfo* pDynamicState

指向动态状态的创建信息

VkPipelineLayout pipelineLayout

管线布局的handle

VkRenderPass renderPass

渲染通道的handle

uint32_t subpass

指明使用了该管线的子通道在renderPass所示渲染通道中的索引

VkPipeline basePipelineHandle

如果该管线从一个已经创建了的管线衍生而来,提供已创建管线的handle

int32_t basePipelineIndex

见后文

  • 如果该管线从vkCreateGraphicsPipelines(...)中,由pCreateInfos所指数组元素所明确的另一个需要被创建的管线衍生而来,提供另一管线创建信息在pCreateInfos所指数组中的索引basePipelineIndex。该索引必须小于当前管线创建信息的索引,即vkCreateGraphicsPipelines(...)依序创建pCreateInfos数组元素对应的管线,而当前管线只能从早于其创建的管线衍生而来。

  • 若需要衍生,但并非从同一批创建的管线中较早创建的管线衍生而来,basePipelineIndex应为特殊值-1,而不是0,因为0是一个有效的索引。

  • flags中未包含VK_PIPELINE_CREATE_DERIVATIVE_BIT时,basePipelineHandle和basePipelineIndex被无视。

版本要求

VkPipelineCreateFlagBits 的枚举项

1.0

VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT 表示禁止优化

1.0

VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT 表示允许从该管线衍生出其他管线

1.0

VK_PIPELINE_CREATE_DERIVATIVE_BIT 表示该管线是从其他管线衍生而来

1.1

VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT 见后文

1.1

VK_PIPELINE_CREATE_DISPATCH_BASE_BIT 表示绑定计算管线后可以使用vkCmdDispatchBase(...)指令,仅用于计算管线

1.3

VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT 见后文

1.3

VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT 见后文

  • VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT表示着色器中所有的gl_ViewIndex输入(需要GLSL语言扩展GL_EXT_multiview,适用于开启多视点multiview的情况)都采取与gl_DeviceIndex输入(需要GLSL语言扩展GL_EXT_device_group,适用于开启device group的情况,device group是由单个逻辑设备代表多个物理设备的Vulkan扩展功能)一致的数值。

  • 若创建管线时使用了着色器模组标识符(shader module identifier,需要用到VK_EXT_shader_module_identifier扩展),并在flags中指定了VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT,但结果无法根据标识符找到管线缓存,那么管线创建将会失败,并返回VK_PIPELINE_COMPILE_REQUIRED(注意,这个VkResult枚举项是大于0的,不被算作是个错误代码)。

  • VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT影响vkCreateGraphicsPipelines(...)或vkCreateComputePipelines(...)中pCreateInfos所指代其他管线的创建,若当前管线创建信息的flags中包含该bit,且当前管线创建失败,则后续其他管线亦不会被创建。

VkPipelineVertexInputStateCreateInfo

顶点输入状态用于指定顶点数据的内容,以及以如何输入顶点数据。

struct VkPipelineVertexInputStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO

const void* pNext

指向一个用于扩展该结构体的结构体

VkPipelineVertexInputStateCreateFlags flags

如有必要,指向一个用于扩展该结构体的结构体

uint32_t vertexBindingDescriptionCount

顶点输入绑定描述的数量

const VkVertexInputBindingDescription* pVertexBindingDescriptions

指向VkVertexInputBindingDescription的数组,用于指定顶点输入绑定

uint32_t vertexAttributeDescriptionCount

顶点属性描述的数量

const VkVertexInputAttributeDescription* pVertexAttributeDescriptions

指向VkVertexInputAttributeDescription的数组,用于指定顶点属性

struct VkVertexInputBindingDescription 的成员说明

uint32_t binding

指定顶点输入对应顶点缓冲区的绑定位置

uint32_t stride

每个顶点的数据之间的步长间隔,单位为字节

VkVertexInputRate inputRate

输入频率

  • 注意这里的binding对应vkCmdBindVertexBuffers(...)中的binding,跟着色器中的binding修饰符无关,着色器中的binding修饰符不用于修饰顶点输入。

  • 允许指定binding的意义在于减少使用vkCmdBindVertexBuffers(...)进行绑定缓冲区的行为。即,你可以一次性绑定多个顶点缓冲区到不同的binding,然后不同的管线去取用不同binding对应的数据,而不是每次切换管线时,都特地把所需的缓冲区绑定到从0号binding起始的位置上。

  • inputRate可为VK_VERTEX_INPUT_RATE_VERTEXVK_VERTEX_INPUT_RATE_INSTANCE,前者表示逐顶点输入,后者表示逐实例输入。

struct VkVertexInputAttributeDescription 的成员说明

uint32_t location

指定这个顶点属性输入到着色器中哪个location

uint32_t binding

指定这个顶点属性的数据,对应所用顶点缓冲区的绑定位置

VkFormat format

这个顶点属性的格式

uint32_t offset

这个顶点属性在相应顶点缓冲区的单组顶点数据中,距离起始的位置,单位为字节

  • 注意binding和location不是二级结构,互不隶属。

来举例说明VkVertexInputBindingDescriptionVkVertexInputAttributeDescription的作用。
先来简单解释下逐顶点输入和逐实例输入,如果我要绘制两个三角形,然后我有3个顶点数据描述一个具体的三角形,将它们逐顶点输入,2个位置数据描述三角形各自的位置,将它们逐实例输入,于是如果我用vkCmdDraw(commandBuffer, 3, 2, 0, 0)绘制,将产生6个顶点,每3个顶点构成一个三角形。
一个较简单的顶点着色器代码将会如下:

#version 460
#pragma shader_stage(vertex)

layout(location = 0) in vec2 i_VertexPosition;//逐顶点
layout(location = 1) in vec2 i_InstancePosition;//逐实例

void main() {
    gl_Position = vec4(i_VertexPosition + i_InstancePosition, 1);
}

为了说明问题,我让上述代码再复杂一点,逐顶点输入贴图坐标:

#version 460
#pragma shader_stage(vertex)

layout(location = 0) in vec2 i_VertexPosition;//逐顶点
layout(location = 1) in vec2 i_VertexTexCoord;//逐顶点
layout(location = 2) in vec2 i_InstancePosition;//逐实例
layout(location = 0) out vec2 o_UV;

void main() {
    gl_Position = vec4(i_VertexPosition + i_InstancePosition, 1);
    o_UV = i_VertexTexCoord;
}

对应的C++结构体会是:

//逐顶点输入的数据
struct perVertex {
    vec2 position;
    vec2 texCoord;
};
//逐实例输入的数据
struct perInstance {
    vec2 position;
};

逐顶点输入的数据和逐实例输入的数据会放在其各自的顶点缓冲区中。
而上文结构体中的不同成员则占据各自的location。
于是为上述情形书写顶点输入绑定描述和顶点属性描述:

VkVertexInputBindingDescription vertexInputBindings[2] = {
    { 0, sizeof(perVertex), VK_VERTEX_INPUT_RATE_VERTEX },
    { 1, sizeof(perInstance), VK_VERTEX_INPUT_RATE_INSTANCE },
};
VkVertexInputAttributeDescription vertexInputAttributes[3] = {
    { 0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(perVertex, position) },
    { 1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(perVertex, texCoord) },
    { 2, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(perInstance, position) },
};
  • VK_FORMAT_R32G32_SFLOAT表示有两个32位浮点数分量,对应vec2。关于格式的具体说明,参见图像及数据的格式

VkPipelineInputAssemblyStateCreateInfo

输入装配状态主要用于指定输入的图元拓扑类型。

struct VkPipelineInputAssemblyStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO

const void* pNext

指向一个用于扩展该结构体的结构体

VkPipelineInputAssemblyStateCreateFlags flags

VkPrimitiveTopology topology

输入的图元拓扑类型

VkBool32 primitiveRestartEnable

是否允许重启图元,见后文

  • 若primitiveRestartEnable为VK_TRUE,则允许在索引绘制中,用一个特殊的索引来重启图元,这个索引的数值为所指定无符号索引类型的最大值,比如,若使用索引类型为VK_INDEX_TYPE_UINT16,则为0xffff。

版本要求

VkPrimitiveTopology 的枚举项

1.0

VK_PRIMITIVE_TOPOLOGY_POINT_LIST 表示输入点

1.0

VK_PRIMITIVE_TOPOLOGY_LINE_LIST 表示输入线段,每两个点构成一条线段

1.0

VK_PRIMITIVE_TOPOLOGY_LINE_STRIP 表示输入线段,每个点(若该点非最初的点)和其前一个点构成线段

1.0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST 表示输入三角形,每三个点构成一个三角形

1.0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP 表示输入三角形,每个点(若该点非最初的两点之一)和其前两个点构成三角形

1.0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN 表示输入三角形,每个点(若该点非最初的两点之一)和其前一个点及最初的点构成三角形

1.0

VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY

1.0

VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY

1.0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY

1.0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY

1.0

VK_PRIMITIVE_TOPOLOGY_PATCH_LIST 表示以patch形式输入顶点,用于涉及到细分(细分控制和细分求值着色器)的管线

  • VkPrimitiveTopology仅仅指定输入的图元拓扑类型,实际生成的图元拓扑类型可以被几何着色器改变,若输入patch,则由细分求值着色器决定生成的图元拓扑类型。

  • 用简短的文字难以说明各类WITH_ADJACENCY的图元拓扑类型的规则,有兴趣请自行了解。

举例而言:
绘制一个四边形,若使用VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,需要6个顶点,或4个顶点和6个索引,若使用VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,则只需要4个顶点。
若图元拓扑类型为VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,由于每个点和其前一个点构成线段,若不重启图元,则绘制出的应当是不间断的折线;通过重启图元,可以使用一个绘制命令来绘制多条折线。

VkPipelineTessellationStateCreateInfo

细分状态用于在进行细分的管线中,指定控制点的数量。

struct VkPipelineTessellationStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineTessellationStateCreateFlags flags

uint32_t patchControlPoints

指定每个patch中控制点的个数

  • patchControlPoints的上限不超过VkPhysicalDeviceLimits::maxTessellationPatchSize。

如果管线中没有细分控制和细分求值着色器,且输入图元拓扑类型非VK_PRIMITIVE_TOPOLOGY_PATCH_LIST,无视patchControlPoints。

VkPipelineViewportStateCreateInfo

视口状态用于指定视口和剪裁范围。

struct VkPipelineViewportStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineViewportStateCreateFlags flags

uint32_t viewportCount

指定视口的个数

const VkViewport* pViewports

指向VkViewport的数组,用于指定视口

uint32_t scissorCount

指定剪裁范围的个数

const VkRect2D* pScissors

指向VkRect2D的数组,用于指定剪裁范围

struct VkViewport 的成员说明

float x

视口左上角相对于图像附件左上角位置的横坐标,单位是像素

float y

视口左上角相对于图像附件左上角位置的纵坐标,单位是像素

float width

视口宽度,单位是像素

float height

视口高度,单位是像素

float minDepth

最小深度值,见后文

float maxDepth

最大深度值,见后文

  • 若没有开启设备特性中的multiViewport(多视口),那么你只能且必须使用一组视口和剪裁范围。使用多视口时,每个视口与剪裁范围一一对应。渲染到哪个视口需在几何着色器中用gl_ViewportIndex指定。

  • 如果你想在渲染命令过程中视情况动态地设置视口或剪裁范围(使用动态状态),那么VkPipelineViewportStateCreateInfo中指定的视口或剪裁范围被无视。

VkViewport定义一个映射关系。举例说明:
若图像附件大小为1280*720,VkViewport中x和y分别为320和180,视口大小为640*360,则内容会被渲染到区域中央640*360大小的范围内。
这是一种不必改变着色器中的投影矩阵即可改变渲染区域的方法。注意,像素的位置做了映射,但不代表原本gl_Position的x和y在[-1, 1]范围外的像素就会出现在视口之外。
minDepth和maxDepth也是进行映射,若minDepth为0,maxDepth为0.5f,则gl_Depth为0.5f的话,输出到深度附件的数值为0.25f。
minDepth可以大于maxDepth(这俩名字起得不好),若minDepth为1,maxDepth为0,则映射的结果为1-gl_Depth,这显然会反转深度测试的结果。
除非使用VK_EXT_depth_range_unrestricted这一扩展,否则minDepth和maxDepth的数值应当在[0, 1],即Vulkan默认的深度范围内。

剪裁范围用VkRect2D指定,即你需要指定剪裁范围相对于图像附件左上角的位置,以及剪裁范围的大小,单位同样是像素。

最后,能显示出像素的区域为视口和对应索引的剪裁范围的交集。

VkPipelineRasterizationStateCreateInfo

栅格化状态指定在栅格化阶段及该阶段前的操作。

struct VkPipelineRasterizationStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineRasterizationStateCreateFlags flags

VkBool32 depthClampEnable

指定是否钳制深度

VkBool32 rasterizerDiscardEnable

指定是否在栅格化阶段前丢弃图元

VkPolygonMode polygonMode

三角形绘制模式

VkCullModeFlags cullMode

面剔除模式

VkFrontFace frontFace

指定正面顶点顺序是顺时针还是逆时针,可为VK_FRONT_FACE_COUNTER_CLOCKWISE(逆时针,默认)或VK_FRONT_FACE_CLOCKWISE(顺时针)

VkBool32 depthBiasEnable

是否开启深度偏移,注:深度偏移主要是为了解决实现阴影时的Shadow Acne问题(物体自身的阴影在其表面形成纹路等情形)

float depthBiasConstantFactor

深度偏移常量系数

float depthBiasClamp

深度偏移钳制

float depthBiasSlopeFactor

深度偏移坡度系数

float lineWidth

指定绘制线段时的线宽,单位是像素,没有开启wideLines设备特性的话必须为1

  • 如果depthClampEnable为VK_TRUE,若gl_Depth的数值在[0, 1]之外,则在映射到VkViewport所指定的深度范围前,钳制到0或1。

  • 如果rasterizerDiscardEnable为VK_TRUE,则在栅格化阶段前丢弃图元,这意味着不会执行片段着色器,不向图像附件输出新的数值。丢弃图元的意义在于仅使用着色器的其他作用,比如在几何或细分求值着色器中将顶点数据写入storage buffer。

  • 深度偏移为:depthBiasConstantFactor * r + depthBiasSlopeFactor * m,其中r是深度缓冲中的最小可表示正数,数值取决于深度缓冲的格式,m为片段的视口深度在x和y方向的偏导数平方和的开方,或可能为x和y方向的偏导数绝对值的较大值。
    注:视口变换发生在栅格化阶段前,所以被求导的是视口变换后的深度,不是NDC深度。

  • 当depthBiasClamp非0时,对depthBiasConstantFactor和depthBiasSlopeFactor的合计效果进行钳制:若depthBiasClamp大于0,则合计的偏移值不超过depthBiasClamp;若depthBiasClamp小于0,则合计的偏移值不小于depthBiasClamp。

版本要求

VkPolygonMode 的枚举项

1.0

VK_POLYGON_MODE_FILL 表示绘制实心三角形

1.0

VK_POLYGON_MODE_LINE 表示绘制三角形的线框

1.0

VK_POLYGON_MODE_POINT 表示绘制三角形的顶点

  • 虽然VkPolygonMode用了“polygon”这个词,但无论你绘制的是TRIANGLE_LIST还是TRIANGLE_STRIP,绘制线框时,会绘制位于多边形内部的三角形的边。

版本要求

VkCullModeFlagBits 的枚举项

1.0

VK_CULL_MODE_NONE 表示无面剔除

1.0

VK_CULL_MODE_FRONT_BIT 表示剔除正面

1.0

VK_CULL_MODE_BACK_BIT 表示剔除背面

1.0

VK_CULL_MODE_FRONT_AND_BACK 表示剔除正面和背面,相当于VK_CULL_MODE_FRONT_BIT | VK_CULL_MODE_BACK_BIT

VkPipelineMultisampleStateCreateInfo

多重采样状态,顾名思义。

struct VkPipelineMultisampleStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineMultisampleStateCreateFlags flags

VkSampleCountFlagBits rasterizationSamples

指定每个像素的采样点个数,必须是2的次数,可用值从VK_SAMPLE_COUNT_1_BITVK_SAMPLE_COUNT_64_BIT

VkBool32 sampleShadingEnable

指定是否开启sample shading,见后文

float minSampleShading

最小sample shading系数,在[0, 1]范围内

const VkSampleMask* pSampleMask

见后文

VkBool32 alphaToCoverageEnable

见后文

VkBool32 alphaToOneEnable

见后文

当rasterizationSamples为VK_SAMPLE_COUNT_1_BIT以外的值时即表示开启多重采样(MSAA)。
记采样点个数为N,开启多重采样后,空间开销为原先的(1+N)倍,这个1来源于解析附件,无多重采样时不需要解析。

Sample shading(采样点着色)对一个像素中的多个采样点执行片段着色。
不开启sample shading时,只会在计算像素的coverage mask(覆盖遮罩,见下文)时选取多个采样点,片段着色器仍旧只对每个像素执行一次(虽然Vulkan标准中没有规定,但通常如此)。因为只有图元边缘的像素可能不被图元完全覆盖,最终只有图元边缘呈现出抗锯齿的效果。
开启sample shading后,选取像素内部一定数量的采样点调用片段着色器,最少的着色次数为minSampleShading * 采样点个数,minSampleShading为1时,即超采样(SSAA)。
注意无论片段着色器对每个像素执行几次,每个像素的采样点个数都是确定的。

VkSampleMask实质上就是uint32_t。Sample mask的比特与采样点一一对应,因为至多64个采样点,pSampleMask所指数组中至多两个元素。
在多重采样中,栅格化时会计算采样点是否被图元覆盖,得到初始的coverage mask。在4xMSAA中,四个采样点皆被图元覆盖时的初始coverage mask为0b1111。
此处指定的sample mask会与coverage mask做位与,这一步叫做sample mask test,发生在栅格化阶段。显然,若在此处把某个索引的采样点对应的bit指定为0,就意味着直接把该索引的采样点一概算作不被覆盖了。
在执行片段着色器前,无论是否经历sample mask test,若一个片段(非MSAA的像素,或MSAA中包含多个采样点的像素)的所有采样点coverage mask为0,则该片段被丢弃,不进行后续其他操作。

Note

关于单个采样点的coverage mask的相应bit为0时,是否还会执行其片段着色器:
Vulkan官方标准中,对于各类测试不通过时的结果,采用的说法是“coverage为0”,在OpenGL的官方wiki上,采样的说法多为“discard(丢弃)”,显然Vulkan官方标准采用了较为保守的说法。
上述两个概念通常不需要区分,除非你想要使用着色器的副作用,比如写入storage缓冲区。
你可以开启sample shading后,用如下GLSL代码进行验证,count_covered和count_notCovered为storage缓冲区中的变量,写入结束后在CPU侧读取其数值,若对不被覆盖的采样点不执行片段着色器,那么count_notCovered应该总是0:
if (gl_SampleMaskIn[0] == 1 << gl_SampleID) atomicAdd(count_covered, 1); else atomicAdd(count_notCovered, 1);
↑你可以顺带验证一下gl_SampleMaskIn[0]的数值。

若alphaToCoverageEnable为VK_TRUE,则会在执行片段着色器后,根据输出的各个采样点的A通道值,生成一个遮罩与片段着色器后的coverage mask(应用可能发生的sample mask test及片段着色器中可能输出的gl_SampleMask后得到的结果)做位与,A通道值到遮罩的转换是实现特定的,标准仅规定A通道为0时生成的遮罩为0。
若alphaToOneEnable为VK_TRUE,则会在执行片段着色器后,无视输出颜色的A通道,使得采样点的A通道值为1,这可以在alphaToCoverageEnable为VK_TRUE时使像素的A通道值不至于过低。

注意,虽然名称很相似,alphaToCoverageEnable影响coverage mask,而alphaToOneEnable影响A通道。Alpha to coverage的效果在alpha to one前应用,即alpha to one使得采样点A通道值为1并不影响coverage mask。
alphaToCoverageEnable和alphaToOneEnable都只影响片段着色器输出到0号索引的色值,关于输出到0号索引,见Dual-source Blending

Note

关于alpha to coverage的效果:
即便你给所有采样点一个相同的A通道值,且不做sample mask test,若A通道值不是整数/采样点个数,最终呈现的图像中,各个像素点可能是不同的透明度。就我的Nvidia 1060 Max-Q显卡而言,图像整体算是均匀的,但细看会察觉到“棋盘格”或“网格”,类似“半调图案”的效果。Vulkan标准中允许底层实现对不同坐标的片段采用不同的算法(The algorithm may be different at different framebuffer coordinates.)以规避一些图像瑕疵(artifact),在为特定平台开发时你可以考虑利用这一效果。

VkPipelineDepthStencilStateCreateInfo

深度模板状态指定是否开启深度和模板测试,以及如何比较深度和模板值。

struct VkPipelineDepthStencilStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineDepthStencilStateCreateFlags flags

VkBool32 depthTestEnable

是否开启深度测试

VkBool32 depthWriteEnable

开启深度测试时,是否在测试通过后写入深度

VkCompareOp depthCompareOp

深度值的比较方式

VkBool32 depthBoundsTestEnable

是否开启depth bounds test

VkBool32 stencilTestEnable

是否开启模板测试

VkStencilOpState front

对正面及点线片元做测试时的模板状态

VkStencilOpState back

对背面片元做测试时的模板状态

float minDepthBounds

开启depth bounds test时,深度边界的最小值

float maxDepthBounds

开启depth bounds test时,深度边界的最大值

struct VkStencilOpState 的成员说明

VkStencilOp failOp

模板测试失败时的选项

VkStencilOp passOp

模板测试通过时的选项

VkStencilOp depthFailOp

模板测试通过但深度测试失败时的选项

VkCompareOp compareOp

模板值的比较方式

uint32_t compareMask

比较模板值前,该遮罩与引用的模板值做位与,将结果用于比较

uint32_t writeMask

写入模板值时,将引用的模板值覆盖到模板缓冲中由此遮罩指定的相应比特位

uint32_t refernece

引用的模板值,见后文

版本要求

VkCompareOp 的枚举项

1.0

VK_COMPARE_OP_NEVER 表示无论深度值(或模板值,若语境为模板测试)如何,一定不通过测试

1.0

VK_COMPARE_OP_LESS 表示若新片段的深度值小于深度模板附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_EQUAL 表示若新片段的深度值等于DS附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_LESS_OR_EQUAL 表示若新深度值小于等于DS附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_GREATER 表示若新深度值大于DS附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_NOT_EQUAL 表示若新深度值不等于DS附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_GREATER_OR_EQUAL 表示若新深度值大于等于DS附件中已有的深度值时,通过深度测试

1.0

VK_COMPARE_OP_ALWAYS 表示无论深度值如何,一定通过深度测试

只有开启深度测试时即深度写入,且深度测试通过时,新片段的深度会被写入到深度缓冲。未通过深度测试的片段会被丢弃(对于多重采样的情况,Vulkan标准中的说法为使得采样点的coverage为0)。

深度边界测试(depth bounds test)的测试方式是:若已在深度缓冲中的深度值在[minDepthBounds, maxDepthBounds]之内,执行对当前片段的模板和深度测试及片段着色。
注意,用于比较的不是新片段的深度值。这似乎很反直觉,该测试意义在于减少一些后续处理,比如在延迟渲染中,可以用这一方式限定光照距离。
若不使用VK_EXT_depth_range_unrestricted这一扩展,则minDepthBounds和maxDepthBounds的数值应该在[0, 1]之内。
minDepthBounds和maxDepthBounds也可以被动态地设置,见后文关于动态状态的说明。

版本要求

VkStencilOp 的枚举项

1.0

VK_STENCIL_OP_KEEP 表示不改变模板缓冲中原有的数值

1.0

VK_STENCIL_OP_ZERO 表示使用0作为写入值

1.0

VK_STENCIL_OP_REPLACE 表示使用reference作为写入值

1.0

VK_STENCIL_OP_INCREMENT_AND_CLAMP 表示使用DS附件中原有数值+1作为写入值,并钳制到255

1.0

VK_STENCIL_OP_DECREMENT_AND_CLAMP 表示使用DS附件中原有数值-1作为写入值,并钳制到0

1.0

VK_STENCIL_OP_INVERT 表示将DS附件中原有数值按位取反,以该数值为写入值

1.0

VK_STENCIL_OP_INCREMENT_AND_WRAP 表示使用DS附件中原有数值+1作为写入值,若原有数值为255,写入值为0

1.0

VK_STENCIL_OP_DECREMENT_AND_WRAP 表示使用DS附件中原有数值-1作为写入值,若原有数值为0,写入值为255

模板测试的测试方式是:
将模板缓冲中已有的数值Sa和引用值Sr(即reference)与compareMask作位与,得到Sa'和Sr',根据compareOp作比较。
若模板测试通过,则继续做深度测试。根据模板测试是否通过及深度测试是否通过,从failOp、passOp、depthFailOp中选择如何生成写入值Sg。
最终,覆盖到DS附件中的数值为 (writeMask & Sg) | (~writeMask & Sa)。

关于深度测试的实际使用,参见Ch8-2 深度测试和深度可视化
关于模板测试的实际使用,参见//TODO

VkPipelineColorBlendStateCreateInfo

混色状态,指定如何混色,或对颜色数值运用逻辑运算。

struct VkPipelineColorBlendStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineColorBlendStateCreateFlags flags

VkBool32 logicOpEnable

是否开启逻辑运算

VkLogicOp logicOp

指定进行何种逻辑运算

uint32_t attachmentCount

混色方式的数量

const VkPipelineColorBlendAttachmentState* pAttachments

指向VkPipelineColorBlendAttachmentState的数组,用于为每个颜色附件指定混色方式

float blendConstants[4]

VkPipelineColorBlendAttachmentState中的混色因子涉及到常量,在此按RGBA顺序指定

版本要求

VkLogicOp 的枚举项(记新生成片元的任一通道值为s,颜色附件中已有的对应通道值为d)

1.0

VK_LOGIC_OP_CLEAR 表示将结果数值设置为0

1.0

VK_LOGIC_OP_SET 表示结果数值的所有比特位皆为1

1.0

VK_LOGIC_OP_COPY 表示s

1.0

VK_LOGIC_OP_NO_OP 表示d

1.0

VK_LOGIC_OP_COPY_INVERTED 表示!s

1.0

VK_LOGIC_OP_INVERT 表示!d

1.0

VK_LOGIC_OP_AND 表示s&d

1.0

VK_LOGIC_OP_OR 表示s|d

1.0

VK_LOGIC_OP_XOR 表示s^d

1.0

VK_LOGIC_OP_NAND 表示!(s&d)

1.0

VK_LOGIC_OP_NOR 表示!(s|d)

1.0

VK_LOGIC_OP_EQUIVALENT 表示!(s^d)

1.0

VK_LOGIC_OP_AND_REVERSE 表示s&!d

1.0

VK_LOGIC_OP_AND_INVERTED 表示!s&d

1.0

VK_LOGIC_OP_OR_REVERSE 表示s|!d

1.0

VK_LOGIC_OP_OR_INVERTED 表示!s|d

逻辑运算只能用于整数类型(SIGNED/UNSIGNED/NORMALIZED)的颜色附件,不能应用于浮点或SRGB类型。
要启用逻辑运算,除此之外还需事先在设备特性中开启逻辑运算(将VkPhysicalDeviceFeatures::logicOp设置为VK_TRUE)。
开启逻辑运算时,无论VkPipelineColorBlendAttachmentState::blendEnable是否为VK_TRUE,一律关闭混色。

struct VkPipelineColorBlendAttachmentState 的成员说明

VkBool32 blendEnable

是否开启混色

VkBlendFactor srcColorBlendFactor

对新生成片元的RGB通道采用的混色因子

VkBlendFactor dstColorBlendFactor

对颜色附件中已有的RGB通道采用的混色因子

VkBlendOp colorBlendOp

对RGB通道的混色选项(运算方式)

VkBlendFactor srcAlphaBlendFactor

对新生成片元的Alpha通道采用的混色因子

VkBlendFactor dstAlphaBlendFactor

对颜色附件中已有的Alpha通道采用的混色因子

VkBlendOp alphaBlendOp

对A通道的混色选项(运算方式)

VkColorComponentFlags colorWriteMask

指示是否向RGBA各通道写入数值的位遮罩,RGBA分别对应0b0001、0b0010、0b0100、0b1000

  • 关闭混色时,新生成的色值会被直接覆写到颜色附件。

  • colorWriteMask的作用与是否开启混色无关。

版本要求

VkBlendOp 的枚举项(记新生成片元的任一通道值为s,颜色附件中已有的对应通道值为d,混色因子分别为fs和fd)

1.0

VK_BLEND_OP_ADD 表示s*fs+d*fd

1.0

VK_BLEND_OP_SUBTRACT 表示s*fs-d*fd

1.0

VK_BLEND_OP_REVERSE_SUBTRACT 表示d*fd-s*fs

1.0

VK_BLEND_OP_MIN 对s和d中的各通道选取较小值,该选项无需混色因子

1.0

VK_BLEND_OP_MAX 对s和d中的各通道选取较大值,该选项无需混色因子

版本要求

VkBlendFactor 的枚举项(记新生成片元的颜色值为s.rgba,另一混色来源的颜色值为s1.rgba,颜色附件中已有的颜色值为d.rgba)

1.0

VK_BLEND_FACTOR_ZERO 表示对任意通道应用0

1.0

VK_BLEND_FACTOR_ONE 表示对任意通道应用1

1.0

VK_BLEND_FACTOR_SRC_COLOR 表示对RGBA四通道分别应用s.rgba

1.0

VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR 表示对RGBA四通道分别应用1-s.rgba

1.0

VK_BLEND_FACTOR_DST_COLOR 表示对RGBA四通道分别应用d.rgba

1.0

VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR 表示对RGBA四通道分别应用1-d.rgba

1.0

VK_BLEND_FACTOR_SRC_ALPHA 表示对任意通道应用s.a

1.0

VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA 表示对任意通道应用1-s.a

1.0

VK_BLEND_FACTOR_DST_ALPHA 表示对任意通道应用d.a

1.0

VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA 表示对任意通道应用1-d.a

1.0

VK_BLEND_FACTOR_CONSTANT_COLOR 表示对RGBA四通道分别应用blendConstants.rgba

1.0

VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR 表示对RGBA四通道分别应用1-blendConstants.rgba

1.0

VK_BLEND_FACTOR_CONSTANT_ALPHA 表示对任意通道应用blendConstants.a

1.0

VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA 表示对任意通道应用1-blendConstants.a

1.0

VK_BLEND_FACTOR_SRC_ALPHA_SATURATE 表示对RGB通道应用min(s.a, 1-d.a),对a通道使用1

1.0

VK_BLEND_FACTOR_SRC1_COLOR 表示对RGBA四通道分别应用s1.rgba

1.0

VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR 表示对RGBA四通道分别应用1-s1.rgba

1.0

VK_BLEND_FACTOR_SRC1_ALPHA 表示对任意通道应用s1.a

1.0

VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA 表示对任意通道应用1-s1.a

关于混色方式的实际使用案例,参见Ch8-4 预乘Alpha

VkPipelineDynamicStateCreateInfo

动态状态是可以在录制命令缓冲区时指定,而不必在创建管线时决定的状态。

struct VkPipelineDynamicStateCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineDynamicStateCreateFlags flags

uint32_t dynamicStateCount

指定动态状态的个数

const VkDynamicState* pDynamicStates

指向VkDynamicState的数组,用于指定哪些状态将会是动态状态

版本要求

VkDynamicState 的枚举项

1.0

VK_DYNAMIC_STATE_VIEWPORT 对应视口

1.0

VK_DYNAMIC_STATE_SCISSOR 对应剪裁范围

1.0

VK_DYNAMIC_STATE_LINE_WIDTH 对应线宽

1.0

VK_DYNAMIC_STATE_DEPTH_BIAS 对应深度偏移常量系数、深度偏移钳制、深度偏移坡度系数

1.0

VK_DYNAMIC_STATE_BLEND_CONSTANTS 对应混色常量

1.0

VK_DYNAMIC_STATE_DEPTH_BOUNDS 对应深度钳制范围

1.0

VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK 对应模板测试中的compareMask

1.0

VK_DYNAMIC_STATE_STENCIL_WRITE_MASK 对应模板测试中的writeMask

1.0

VK_DYNAMIC_STATE_STENCIL_REFERENCE 对应模板测试中的引用值

1.3

VK_DYNAMIC_STATE_CULL_MODE 对应剔除模式

1.3

VK_DYNAMIC_STATE_FRONT_FACE 对应正面的顶点时针顺序

1.3

VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY 对应图元拓扑类型

1.3

VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT 对应视口和视口的数量

1.3

VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT 对应剪裁范围和剪裁范围的数量

1.3

VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE 对应顶点输入绑定描述中,每个顶点数据的步长间隔

1.3

VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE 对应是否开启深度测试

1.3

VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE 对应是否开启深度写入

1.3

VK_DYNAMIC_STATE_DEPTH_COMPARE_OP 对应深度的比较方式

1.3

VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE 对应是否开启深度边界测试

1.3

VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE 对应是否开启模板测试

1.3

VK_DYNAMIC_STATE_STENCIL_OP 对应模板测试的faliOp、passOp、depthFailOp、compareOp

1.3

VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE 对应是否在栅格化阶段前丢弃图元

1.3

VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE 对应是否开启深度偏移

1.3

VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE 对应是否允许重启图元

  • 指定了动态状态后,在VkGraphicsPipelineCreateInfo中其他创建信息结构体中的相应参数会被无视。

  • 同在创建管线时指定的模板测试参数不同,VK_DYNAMIC_STATE_STENCIL_COMPARE_MASKVK_DYNAMIC_STATE_STENCIL_WRITE_MASKVK_DYNAMIC_STATE_STENCIL_REFERENCEVK_DYNAMIC_STATE_STENCIL_OP等动态状态指定的模板参数有两套,相关命令函数中具有类型为VkStencilFaceFlagBits的参数,使得你能为三角形正面和反面分别指定模板测试参数。

封装为graphicsPipelineCreateInfoPack类

VKBase+.h(如不存在则创建,并在其中包含VKBase.h),vulkan命名空间中添加以下代码:

struct graphicsPipelineCreateInfoPack {
    VkGraphicsPipelineCreateInfo createInfo =
    { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
    std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
    //Vertex Input
    VkPipelineVertexInputStateCreateInfo vertexInputStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
    std::vector<VkVertexInputBindingDescription> vertexInputBindings;
    std::vector<VkVertexInputAttributeDescription> vertexInputAttributes;
    //Input Assembly
    VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
    //Tessellation
    VkPipelineTessellationStateCreateInfo tessellationStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO };
    //Viewport
    VkPipelineViewportStateCreateInfo viewportStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
    std::vector<VkViewport> viewports;
    std::vector<VkRect2D> scissors;
    uint32_t dynamicViewportCount = 1;//动态视口/剪裁不会用到上述的vector,因此动态视口和剪裁的个数向这俩变量手动指定
    uint32_t dynamicScissorCount = 1;
    //Rasterization
    VkPipelineRasterizationStateCreateInfo rasterizationStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
    //Multisample
    VkPipelineMultisampleStateCreateInfo multisampleStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
    //Depth & Stencil
    VkPipelineDepthStencilStateCreateInfo depthStencilStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
    //Color Blend
    VkPipelineColorBlendStateCreateInfo colorBlendStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
    std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
    //Dynamic
    VkPipelineDynamicStateCreateInfo dynamicStateCi =
    { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
    std::vector<VkDynamicState> dynamicStates;
    //--------------------
    graphicsPipelineCreateInfoPack() {
        SetCreateInfos();
        //若非派生管线,createInfo.basePipelineIndex不得为0,设置为-1
        createInfo.basePipelineIndex = -1;
    }
    //移动构造器,所有指针都要重新赋值
    graphicsPipelineCreateInfoPack(const graphicsPipelineCreateInfoPack& other) noexcept {
        createInfo = other.createInfo;
        SetCreateInfos();

        vertexInputStateCi = other.vertexInputStateCi;
        inputAssemblyStateCi = other.inputAssemblyStateCi;
        tessellationStateCi = other.tessellationStateCi;
        viewportStateCi = other.viewportStateCi;
        rasterizationStateCi = other.rasterizationStateCi;
        multisampleStateCi = other.multisampleStateCi;
        depthStencilStateCi = other.depthStencilStateCi;
        colorBlendStateCi = other.colorBlendStateCi;
        dynamicStateCi = other.dynamicStateCi;

        shaderStages = other.shaderStages;
        vertexInputBindings = other.vertexInputBindings;
        vertexInputAttributes = other.vertexInputAttributes;
        viewports = other.viewports;
        scissors = other.scissors;
        colorBlendAttachmentStates = other.colorBlendAttachmentStates;
        dynamicStates = other.dynamicStates;
        UpdateAllArrayAddresses();
    }
    //Getter,这里我没用const修饰符
    operator VkGraphicsPipelineCreateInfo& () { return createInfo; }
    //Non-const Function
    //该函数用于将各个vector中数据的地址赋值给各个创建信息中相应成员,并相应改变各个count
    void UpdateAllArrays() {
        createInfo.stageCount = shaderStages.size();
        vertexInputStateCi.vertexBindingDescriptionCount = vertexInputBindings.size();
        vertexInputStateCi.vertexAttributeDescriptionCount = vertexInputAttributes.size();
        viewportStateCi.viewportCount = viewports.size() ? uint32_t(viewports.size()) : dynamicViewportCount;
        viewportStateCi.scissorCount = scissors.size() ? uint32_t(scissors.size()) : dynamicScissorCount;
        colorBlendStateCi.attachmentCount = colorBlendAttachmentStates.size();
        dynamicStateCi.dynamicStateCount = dynamicStates.size();
        UpdateAllArrayAddresses();
    }
private:
    //该函数用于将创建信息的地址赋值给basePipelineIndex中相应成员
    void SetCreateInfos() {
        createInfo.pVertexInputState = &vertexInputStateCi;
        createInfo.pInputAssemblyState = &inputAssemblyStateCi;
        createInfo.pTessellationState = &tessellationStateCi;
        createInfo.pViewportState = &viewportStateCi;
        createInfo.pRasterizationState = &rasterizationStateCi;
        createInfo.pMultisampleState = &multisampleStateCi;
        createInfo.pDepthStencilState = &depthStencilStateCi;
        createInfo.pColorBlendState = &colorBlendStateCi;
        createInfo.pDynamicState = &dynamicStateCi;
    }
    //该函数用于将各个vector中数据的地址赋值给各个创建信息中相应成员,但不改变各个count
    void UpdateAllArrayAddresses() {
        createInfo.pStages = shaderStages.data();
        vertexInputStateCi.pVertexBindingDescriptions = vertexInputBindings.data();
        vertexInputStateCi.pVertexAttributeDescriptions = vertexInputAttributes.data();
        viewportStateCi.pViewports = viewports.data();
        viewportStateCi.pScissors = scissors.data();
        colorBlendStateCi.pAttachments = colorBlendAttachmentStates.data();
        dynamicStateCi.pDynamicStates = dynamicStates.data();
    }
};
  • 由于所有创建信息结构体都是没有构造函数的聚合体,花括号初始化器列表中未提及的成员变量被零初始化。

创建计算管线

vkCreateComputePipelines(...)创建图形管线:

VkResult VKAPI_CALL vkCreateComputePipelines(...) 的参数说明

VkDevice device

逻辑设备的handle

VkPipelineCache pipelineCache

若非VK_NULL_HANDLE,则提供管线的缓存信息

uint32_t createInfoCount

计算管线的创建信息的个数

const VkComputePipelineCreateInfo* pCreateInfos

指向VkComputePipelineCreateInfo的数组,提供一个或多个管线的创建信息

const VkAllocationCallbacks* pAllocator

VkPipeline* pPipelines

若执行成功,将计算管线的handle写入pPipelines所指数组

struct VkComputePipelineCreateInfo 的成员说明

VkStructureType sType

结构体的类型,本处必须是VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO

const void* pNext

如有必要,指向一个用于扩展该结构体的结构体

VkPipelineCreateFlags flags

VkGraphicsPipelineCreateInfo一段中的说明

VkPipelineShaderStageCreateInfo stage

提供单个计算着色器阶段的创建信息

VkPipelineLayout pipelineLayout

管线布局的handle

VkPipeline basePipelineHandle

如果该管线从一个已经创建了的管线衍生而来,提供已创建管线的handle

int32_t basePipelineIndex

VkGraphicsPipelineCreateInfo一段中的说明

封装为pipeline类

VKBase.h,vulkan命名空间中添加以下代码:

class pipeline {
    VkPipeline handle = VK_NULL_HANDLE;
public:
    pipeline() = default;
    pipeline(VkGraphicsPipelineCreateInfo& createInfo) {
        Create(createInfo);
    }
    pipeline(VkComputePipelineCreateInfo& createInfo) {
        Create(createInfo);
    }
    pipeline(pipeline&& other) noexcept { MoveHandle; }
    ~pipeline() { DestroyHandleBy(vkDestroyPipeline); }
    //Getter
    DefineHandleTypeOperator;
    DefineAddressFunction;
    //Non-const Function
    result_t Create(VkGraphicsPipelineCreateInfo& createInfo) {
        createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        VkResult result = vkCreateGraphicsPipelines(graphicsBase::Base().Device(), VK_NULL_HANDLE, 1, &createInfo, nullptr, &handle);
        if (result)
            outStream << std::format("[ pipeline ] ERROR\nFailed to create a graphics pipeline!\nError code: {}\n", int32_t(result));
        return result;
    }
    result_t Create(VkComputePipelineCreateInfo& createInfo) {
        createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
        VkResult result = vkCreateComputePipelines(graphicsBase::Base().Device(), VK_NULL_HANDLE, 1, &createInfo, nullptr, &handle);
        if (result)
            outStream << std::format("[ pipeline ] ERROR\nFailed to create a compute pipeline!\nError code: {}\n", int32_t(result));
        return result;
    }
};