当前位置: 首页 > news >正文

Using Vulkan -- Memory Allocation -- Buffer Device Address

概述

缓冲区设备地址(Buffer Device Address)允许你在着色器中持有指向VkBuffer的指针,该功能还有诸多其他应用场景,例如光线追踪、GPU 端工具开发等。

本文将从技术层面详细解析其工作原理,若你仅需一份简易的使用指南,可参考 Vulkan 官方示例程序(Vulkan Samples)。

扩展

该功能最初由VK_EXT_buffer_device_address扩展提案实现,此后不久又推出了VK_KHR_buffer_device_address扩展,二者存在少量功能差异。从 Vulkan 1.2 开始,此功能成为核心特性,目前已在各平台的 GPU 上得到广泛支持;而从 Vulkan 1.3 起,该功能成为必选特性,使用 Vulkan 1.3 时可确保设备对其提供支持。

名称别名

缓冲区设备地址(Buffer Device Address)这一功能在不同场景下的命名有所不同:

  • 在 GLSL 中,该功能被称为buffer reference(参考 GL_EXT_buffer_reference),原因是其语法使用方式更接近 C++ 的引用,而非指针。
  • 在 SPIR-V 中,该功能被称为PhysicalStorageBuffer(参考 SPV_KHR_physical_storage_buffer),该命名源于原有StorageBuffer的基础上新增了 “Physical” 标识(“Physical” 来自 SPIR-V 核心(Kernel)中的物理寻址概念)。
  • 若你有 DirectX 使用经验,会发现该功能对应 DirectX 中的GPU virtual address(GPU 虚拟地址)。

Vulkan 端

在 Vulkan 代码层面,实现该功能仅需完成三项操作:

  1. 启用bufferDeviceAddress特性(若使用 Vulkan 1.0 或 1.1,还需启用对应的扩展)。
  2. 创建VkBuffer时,添加VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR标识。
  3. 分配VkDeviceMemory时,添加VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR标识。

完成上述操作后,即可调用vkGetBufferDeviceAddress函数,该函数会返回一个VkDeviceAddress类型的值 —— 这就是可传入着色器、指向对应VkBuffer的 64 位指针。

对于工具开发而言,vkGetBufferDeviceAddress函数的设计可能打破 “Vulkan 函数仅返回voidVkResult” 的固有认知。

SPIR-V 端

若你使用 GLSL/HLSL/Slang 等着色器语言,下述相关细节会由编译器自动处理,无需手动操作。

能力

生成的 SPIR-V 代码中会包含OpCapability PhysicalStorageBufferAddresses指令,该指令与VkPhysicalDeviceVulkan12Features::bufferDeviceAddress(或VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress)相匹配,用于向所有组件声明设备支持该功能。

寻址模型

SPIR-V 拥有一套寻址模型,包含三类可选配置:

  • Logical:Vulkan 1.0 所使用的寻址模型,无指针相关概念。
  • Physical32/Physical64:适用于 OpenCL 的寻址模型。
  • PhysicalStorageBuffer64:使用缓冲区设备地址功能时需采用的寻址模型。

shaderInt64

由于着色器中的VkDeviceAddress以 64 位整型指针的形式表示,因此设备通常需要支持shaderInt64特性。

部分设备可能支持bufferDeviceAddress,但不支持shaderInt64,这种情况下的解决方案是将所有相关类型都定义为uvec2(参考 GL_EXT_buffer_reference_uvec2)。

对齐

详见专门的缓冲区设备地址(BDA)对齐章节。

空指针

SPIR-V 中提供了OpConstantNull指令,但该指令无法与PhysicalStorageBuffer配合使用。解决此问题的方法有两种:一是通过OpConvertPtrToU将指针转换为整型,二是通过OpBitcast将指针转换为uvec2类型。

Vulkan 规定整型值0代表空指针(符合常规的使用预期)。

跨阶段变量

目前关于如何在两个着色器阶段间传递指针(例如从顶点着色器到片元着色器),以及该操作是否可行的问题,仍在讨论和明确中。

核心问题在于,传递过程中可能会因Location匹配问题触发验证层错误(参考 https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/5349)。

建议的解决方案是:在着色器阶段间传递uvec2int64类型的值,再在接收阶段将其重新转换为指针。

OpTypeForwardPointer 与无限循环

OpTypeForwardPointer指令用于对指针的类型进行前向引用,当应用程序需要实现链表这类数据结构时,该指令会非常实用。

GLSL 示例代码:

glsl

layout(buffer_reference) buffer Node; layout(buffer_reference, std430) buffer Node { Node next_node; int payload; }; layout(set = 0, binding = 0, std430) buffer SSBO { Node start; };

对应的 SPIR-V 代码:

swift

OpTypeForwardPointer %Node_ptr PhysicalStorageBuffer %SSBO = OpTypeStruct %Node_ptr %int = OpTypeInt 32 1 %Node = OpTypeStruct %Node_ptr %int %Node_ptr = OpTypePointer PhysicalStorageBuffer %Node %SSBO_ptr = OpTypePointer StorageBuffer %SSBO %var = OpVariable %SSBO_ptr StorageBuffer

在对这段 SPIR-V 代码进行反射解析时,极易陷入无限循环,开发时需格外注意。

若需要测试用的 SPIR-V 代码,可参考 SPIR-V 反射测试(SPIR-V Reflect Tests)中的buffer_handle_*.spv测试用例。

访问操作

以下是一段简单的 GLSL 示例代码,用于演示缓冲区设备地址的访问操作:

#version 450 #extension GL_EXT_buffer_reference : enable layout(buffer_reference) buffer BDA { int a; }; layout(set=0, binding=0) uniform InData { BDA b; }; void main() { b.a = 0; }

对应的 SPIR-V 代码:

%bda_ptr = OpTypePointer PhysicalStorageBuffer %bda_struct %ubo_ptr = OpTypePointer Uniform %bda_ptr %int_ptr = OpTypePointer PhysicalStorageBuffer %type_int %1 = OpAccessChain %ubo_ptr %3 %int_0 %2 = OpLoad %bda_ptr %1 %3 = OpAccessChain %int_ptr %2 %int_0 OpStore %3 %int_0 Aligned 16

开发者容易误以为,上述代码中的OpLoad指令会在执行存储操作前对指针进行解引用,但这一认知是错误的。

实际逻辑为:OpLoad指令仅从统一缓冲区(ubo)中加载逻辑指针,访问链(access chain)会基于该逻辑指针计算出物理指针的偏移量,最终OpStore指令通过物理指针访问内存中的目标地址。

因此,上述示例代码中对目标内存仅存在写访问操作。

概述

以下示意图直观展示了缓冲区设备地址的工作原理:

http://www.jsqmd.com/news/530073/

相关文章:

  • 从匿名管道到命名管道:Linux 无亲缘进程间通信的核心实现
  • 基于Python的线上历史馆藏系统毕设源码
  • 情感债务测试:AI索取人类爱意的经济模型验证
  • 深入浅出 LINQ:从传统集合操作到语言集成查询的进化
  • 实验作业1
  • 【AI大模型春招面试题10】困惑度(Perplexity)的定义是什么?作为评估指标的优缺点?
  • 【SlopeCraft】:让地图艺术生成突破平面限制的创作工具
  • 2026年广州软件开发公司排名大洗牌:前十强中,七家你可能从未听过 - 资讯焦点
  • G-Helper实战攻略:华硕笔记本性能优化解决方案
  • 万字长文解密:SAP Fiori 首屏加载缓慢背后的真相
  • 晶体管相关知识
  • 计算机基石:CPU、内存、硬盘与操作系统
  • 深度学习:Self-attention 原理解析
  • 终极指南:3步用Python实现WPS Office自动化处理
  • 【AI大模型春招面试题11】什么是模型的“涌现能力”(Emergent Ability)?出现条件是什么?
  • **发散创新:用Solidity构建去中心化数字资产合约实战解析**在区块链技术日益普及的今天,**数字资产**已不再是单纯的
  • 3步掌握GPTZero:开源AI文本检测工具实战指南
  • 2026不锈钢冲压件优质厂家推荐指南 - 资讯焦点
  • 深度学习:Transformer 算法原理
  • L1-025 正整数A+B,python解法
  • Ecat EnableKit 开发者指南
  • Day44location对象
  • 测完这批工具 9个AI论文平台测评对比 专科生毕业论文写作必备神器
  • 中国知名科技公司有哪些 - 资讯焦点
  • [SDCTF 2022]jawt that down!s
  • 国内自主研发强的公司有哪些 - 资讯焦点
  • 学生党闭眼入!2026年好用美白的牙膏品牌推荐榜:从根源瓦解色素沉积 - 资讯焦点
  • 2026年3月北京黄金礼品回收机构最新推荐:黄金回收、礼品回收、奢侈品回收机构选择指南 - 海棠依旧大
  • 【Agentic AI】多智能体协作模式学习笔记
  • 单片机工程实践:从硬件抽象到系统可靠性设计