健壮性

健壮性意味着什么

当 Vulkan 应用程序尝试访问(加载、存储或执行原子操作)它无权访问的内存时,实现必须以某种方式做出反应。在没有健壮性的情况下,这是未定义的行为,甚至允许实现终止程序。如果为访问的内存类型启用了健壮性,则实现必须按照规范的定义以某种方式表现。

robustness_flow.png

何时使用

使用健壮性的一些常见情况是

  1. 需要防止恶意内存访问(例如 WebGPU)。

  2. 无法保证着色器不会越界

  3. 模拟在其他地方观察到的越界行为

重要提示

启用健壮性可能会产生运行时性能成本。应用程序编写者应仔细考虑启用健壮性的影响。

Vulkan 在核心中提供了什么

所有 Vulkan 实现都必须支持 robustBufferAccess 特性。 规范描述了哪些被认为是越界的,以及应该如何处理。实现对于 robustBufferAccess 有一定的灵活性。一个例子是访问 vec4(x,y,z,w),其中 w 值越界,因为规范允许实现决定 xyz 是否也被认为是越界的。

robustBufferAccess 特性有一些限制,因为它仅涵盖缓冲区,而不涵盖图像。它还允许越界写入和原子操作来修改正在访问的缓冲区的数据。对于寻求更强健壮性的应用程序,可以使用 VK_EXT_robustness2

当图像越界时,核心 Vulkan 保证 存储和原子操作对正在访问的内存没有影响。

robustBufferAccess

以下是使用 robustBufferAccess 的示例。(在线尝试

#version 450
layout(set = 0, binding = 0) buffer SSBO {
    // The VkBuffer is only 64 bytes large
    // indexing from [0:63] is valid, rest is OOB
    uint data[128];
};

void main() {
    // will be OOB at runtime
    // will be discarded with robustBufferAccess
    data[96] = 0;

    // will return zero with robustBufferAccess
    uint x = data[127];
}

VK_EXT_image_robustness

robustImageAccess

robustImageAccess 特性在 VK_EXT_image_robustness 中启用对正在访问的图像视图的维度进行越界检查。如果对任何图像进行越界访问,它将返回 (0, 0, 0, 0)(0, 0, 0, 1)

robustImageAccess 特性不保证访问无效 LOD 时返回的值,这仍然是未定义的行为。

VK_EXT_robustness2

某些应用程序,例如从 D3D12 等其他 API 移植的应用程序,需要比 robustBufferAccessrobustImageAccess 提供的更严格的保证。 VK_EXT_robustness2 扩展通过公开 3 个新的健壮性特性来添加此功能,这些特性在以下部分中描述。对于某些实现,这些额外的保证可能会带来性能成本。建议不需要额外健壮性的应用程序尽可能使用 robustBufferAccess 和/或 robustImageAccess

robustBufferAccess2

robustBufferAccess2 特性可以看作是 robustBufferAccess 的超集。

启用该特性后,它可以防止所有越界写入和原子操作修改任何支持缓冲区的内存。 robustBufferAccess2 特性还强制执行当越界访问时,各种类型的缓冲区必须返回的值,如 规范中所述

重要的是从 VkPhysicalDeviceRobustness2PropertiesEXT 查询 robustUniformBufferAccessSizeAlignmentrobustStorageBufferAccessSizeAlignment,因为缓冲区绑定检查的位置的对齐方式在不同实现之间是不同的。

robustImageAccess2

robustImageAccess2 特性可以看作是 robustImageAccess 的超集。它基于对正在访问的图像视图的维度进行越界检查,并对可能返回的值添加了更严格的要求。

使用 robustImageAccess2,对 R、RG 或 RGB 格式的越界访问将返回 (0, 0, 0, 1)。对于 RGBA 格式,例如 VK_FORMAT_R8G8B8A8_UNORM,它将返回 (0, 0, 0, 0)

对于访问超出支持范围的图像 LOD 的情况,启用 robustImageAccess2 后,将被视为越界访问。

nullDescriptor

在未启用 nullDescriptor 功能的情况下,当更新 VkDescriptorSet 时,所有支持它的资源都必须是非空的,即使着色器静态地不使用该描述符。此功能允许描述符由空资源或视图支持。从空描述符加载返回零值,并且对空描述符的存储和原子操作将被丢弃。

nullDescriptor 功能还允许访问 vkCmdBindVertexBuffers::pBuffers 为空的顶点输入绑定。

VK_EXT_pipeline_robustness

由于对于某些实现来说,鲁棒性可能会带来性能成本,因此添加了 VK_EXT_pipeline_robustness 扩展,以允许开发人员仅在需要时请求鲁棒性。

在创建 VkPipeline 时,可以传递一个或多个 VkPipelineRobustnessCreateInfoEXT 结构,以指定对缓冲区、图像和顶点输入资源的访问所需的鲁棒性行为,可以针对整个管线,也可以针对每个管线阶段。

此扩展还提供了 VkPhysicalDevicePipelineRobustnessPropertiesEXT,用于查询实现当未启用鲁棒性功能时提供的默认行为。

VK_EXT_descriptor_indexing

如果处理 VK_EXT_descriptor_indexing 中找到的绑定后更新功能(从 Vulkan 1.2 开始成为核心功能),则必须注意 robustBufferAccessUpdateAfterBind,它指示实现是否可以同时支持 robustBufferAccess 和在绑定后更新描述符的能力。