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

何时使用
使用健壮性的一些常见情况是
-
需要防止恶意内存访问(例如 WebGPU)。
-
无法保证着色器不会越界
-
模拟在其他地方观察到的越界行为
重要提示
启用健壮性可能会产生运行时性能成本。应用程序编写者应仔细考虑启用健壮性的影响。 |
Vulkan 在核心中提供了什么
所有 Vulkan 实现都必须支持 robustBufferAccess
特性。 规范描述了哪些被认为是越界的,以及应该如何处理。实现对于 robustBufferAccess
有一定的灵活性。一个例子是访问 vec4(x,y,z,w)
,其中 w
值越界,因为规范允许实现决定 x
、y
和 z
是否也被认为是越界的。
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 移植的应用程序,需要比 robustBufferAccess
和 robustImageAccess
提供的更严格的保证。 VK_EXT_robustness2 扩展通过公开 3 个新的健壮性特性来添加此功能,这些特性在以下部分中描述。对于某些实现,这些额外的保证可能会带来性能成本。建议不需要额外健壮性的应用程序尽可能使用 robustBufferAccess
和/或 robustImageAccess
。
robustBufferAccess2
robustBufferAccess2 特性可以看作是 robustBufferAccess
的超集。
启用该特性后,它可以防止所有越界写入和原子操作修改任何支持缓冲区的内存。 robustBufferAccess2
特性还强制执行当越界访问时,各种类型的缓冲区必须返回的值,如 规范中所述。
重要的是从 VkPhysicalDeviceRobustness2PropertiesEXT 查询 robustUniformBufferAccessSizeAlignment
和 robustStorageBufferAccessSizeAlignment
,因为缓冲区绑定检查的位置的对齐方式在不同实现之间是不同的。
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
和在绑定后更新描述符的能力。