Vulkan中PushConstant是干嘛的?

阅读次数 38

我想问下PushConstant的作用,在Opengl之前感觉没有见过,它跟UBO的区别是什么?更新频率更快么?有没有更加详细的例子说明下?UBO在某种情况下也能更新,如果是新特性,为什么不直接优化UBO,而要新增一个特性?

2 Answers

Push Constants 是一种快速将少量统一数据提供给着色器的方式。它应该比使用 Uniform Buffer Objects (UBOs) 快得多,但有一个巨大的限制是数据的大小——规范要求为一个 Push Constant 范围提供至少 128 字节的空间。硬件供应商可能会支持更大的大小,但与其他方法相比,这仍然是非常小的(例如 256 字节)。

由于 Push Constants 比其他描述符(我们通过这些资源向着色器提供数据)快得多,因此它们非常适合用于在绘制调用之间变化的数据,比如变换矩阵。

从着色器的角度来看,Push Constants 通过 layout( push_constant ) 限定符和一个统一数据块来声明。例如:

layout( push_constant ) uniform ColorBlock {
  vec4 Color;
} PushConstant;

从应用程序的角度来看,如果着色器想要使用 Push Constants,它们必须在创建管线布局时指定。然后必须将 vkCmdPushConstants() 命令记录到命令缓冲区中。此函数需要一个指向内存的指针,从中将数据复制到 Push Constant 范围。

同一管线的不同着色器阶段可以使用相同的 Push Constant 数据块(类似于 UBOs),或者使用整个范围的一部分。但是,重要的是,每个着色器阶段只能使用一个 Push Constant 数据块。它可以包含多个成员。另一个重要的点是,所有使用 Push Constants 的着色器阶段的总数据大小必须适合大小限制。因此,这个限制不是针对每个阶段,而是针对整个范围。

Vulkan Cookbook 的仓库中有一个示例展示了简单的 Push Constants 使用场景。Sascha Willems 的 Vulkan 示例也包含了一个展示如何使用 Push Constants 的样本。

在Vulkan中,Push Constants是一种快速访问内存的方式,用于将少量数据传递给着色器。它们主要用于传递需要频繁更改的数据,如渲染管线的状态信息、着色器程序参数等。

Push Constant vs UBO(Uniform Buffer Object)

  1. Push Constant

    • Push Constants允许你在CPU上设置一组连续的内存区域,并将其直接推送到GPU。
    • 它们通常比UBO更小(最大为4KB),并且可以更快地更新。
    • 使用Push Constants时,你可以避免显式创建和管理缓冲区对象,从而简化了内存管理和绑定过程。
    • Push Constants的布局是紧密的,这意味着它们没有填充字节,使得数据排列更加紧凑。
    • 在Vulkan中,Push Constants的使用受到限制:它们必须是连续的,并且不能超过4KB。此外,它们必须在渲染命令执行前设置。
  2. Uniform Buffer Object (UBO)

    • UBO是OpenGL中的概念,但在Vulkan中也有类似的机制。
    • UBO允许你将大量数据打包到一个缓冲区中,并通过绑定方式传递给着色器。
    • UBO的大小通常更大,可以达到几MB。
    • 更新UBO可能需要更多的开销,因为需要绑定缓冲区对象并管理其生命周期。
    • UBO支持更复杂的布局,包括填充字节以对齐数据。

更新频率

  • Push Constants更适合频繁更新的数据,因为它们可以直接推送,无需额外的内存管理和绑定操作。
  • UBO适用于需要一次性更新或不那么频繁更新的大块数据。

为什么需要Push Constants

虽然UBO也可以更新,但它们的设计初衷是为了传递相对静态的数据。对于需要频繁更新的小数据集,UBO的更新过程可能会引入不必要的开销。因此,Push Constants提供了一种更高效的方式来处理这类数据。

示例代码

以下是一个简单的Vulkan Push Constant使用的示例:

// 定义Push Constant结构体
struct PushConstants {
    float scale;
    float offset;
};

// 创建Pipeline Layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pSetLayouts = nullptr;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;

VkPushConstantRange pushConstantRange{};
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(PushConstants);

VkPipelineLayout pipelineLayout;
vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout);

// 在渲染命令中设置Push Constant
vkCmdPushConstants(
    commandBuffer,
    pipelineLayout,
    VK_SHADER_STAGE_VERTEX_BIT,
    0,
    sizeof(PushConstants),
    &pushConstants
);

在这个示例中,PushConstants 结构体包含两个浮点数,用于传递缩放和偏移值。在渲染命令中,我们使用 vkCmdPushConstants 函数将这些值直接推送到GPU。

总结

Push Constants提供了一种高效的方式来传递频繁更新的小数据集。它们在设计上与UBO有所不同,适用于不同的场景。虽然UBO也可以更新,但对于需要频繁更改的数据,Push Constants提供了更低的开销和更简洁的API。