[Vulkan 实战] 深入解析 Dynamic Uniform Buffers:高效绘制多物体的利器

在 Vulkan 图形开发中,当我们面对场景中成百上千个需要独立变换矩阵(Model Matrix)的物体时,如何高效地管理 Uniform Buffer 是一个经典难题。

如果我们为每个物体都分配一个独立的VkBufferVkDescriptorSet,不仅会造成大量的内存碎片,更会因为频繁切换 Descriptor Set 而严重拖累 CPU 性能。

Dynamic Uniform Buffers(动态统一缓冲区)提供了一种优雅的中间方案:它允许我们在一个巨大的 Buffer 中紧凑地存储所有物体的数据,并在绘制时通过“动态偏移(Dynamic Offset)”来告诉 Shader 当前使用的是哪一部分数据。

本文将结合 Khronos Vulkan Samples 中的dynamic_uniform_buffers示例,详细拆解其实现流程。Vulkan-Samples/samples/api/dynamic_uniform_buffers at main · KhronosGroup/Vulkan-Samples

核心概念与优势

在标准流程中,Descriptor Set 绑定了 Buffer 的特定范围。而在Dynamic Uniform Buffer中,Descriptor Set 绑定的是整个 Buffer(或一大块范围),但在调用vkCmdBindDescriptorSets时,我们可以额外传递一个动态偏移数组

优势:

  • 减少 Descriptor Set 数量:场景中所有物体可以共用同一个Descriptor Set。

  • 内存连续:数据存储在一个大 Buffer 中,对缓存更友好。

  • 灵活性:可以在绘制循环中快速切换数据源,无需重新分配资源。


最大的坑:内存对齐 (Alignment)

实现 Dynamic Uniform Buffer 最关键、也最容易出错的一步是内存对齐

你不能简单地将glm::mat4(64字节) 紧挨着通过std::vector塞进 Buffer。Vulkan 硬件对动态缓冲区的偏移量有严格的对齐要求,这个值由minUniformBufferOffsetAlignment属性决定(通常是 64 或 256 字节)。

2.1 获取对齐要求

在 C++ 代码中,我们需要手动计算每个物体数据块的步长(Stride):

// 获取设备限制中的最小对齐要求 size_t min_ubo_alignment = static_cast<size_t>(get_device().get_gpu().get_properties().limits.minUniformBufferOffsetAlignment); // 我们的基础数据是一个 4x4 矩阵 dynamic_alignment = sizeof(glm::mat4); // 计算对齐后的实际大小 if (min_ubo_alignment > 0) { dynamic_alignment = (dynamic_alignment + min_ubo_alignment - 1) & ~(min_ubo_alignment - 1); } // 总 Buffer 大小 = 物体数量 * 对齐后的单体大小 size_t buffer_size = OBJECT_INSTANCES * dynamic_alignment;

这段代码确保了dynamic_alignmentminUniformBufferOffsetAlignment的整数倍。

2.2 内存分配

由于 C++ 的newmalloc并不保证按照 GPU 的要求对齐,示例中使用了一个包装函数aligned_alloc来分配 CPU 端的内存:

ubo_data_dynamic.model = static_cast<glm::mat4 *>(aligned_alloc(buffer_size, dynamic_alignment));

描述符设置 (Descriptor Setup)

在设置 Descriptor Set Layout 时,必须明确指定类型为VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC

3.1 Layout 定义

std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = { // Binding 0: 普通 UBO (View/Projection 矩阵) vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ...), // Binding 1: 动态 UBO (Model 矩阵) - 注意这里的类型! vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, VK_SHADER_STAGE_VERTEX_BIT, 1), // ... };

3.2 更新描述符

vkUpdateDescriptorSets时,我们绑定整个动态 Buffer。注意,这里传入的dynamic_alignment实际上可能并未被直接使用作为步长,它主要用于指明单个描述符覆盖的范围(Range),但在动态 Buffer 中,核心在于 Buffer 的 Handle 和总大小。

// 这里的 create_descriptor 帮助函数通常设置 range 为 VK_WHOLE_SIZE 或单个 slot 大小 VkDescriptorBufferInfo dynamic_buffer_descriptor = create_descriptor(*uniform_buffers.dynamic, dynamic_alignment); vkb::initializers::write_descriptor_set( descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, // 类型必须匹配 1, &dynamic_buffer_descriptor );

着色器代码 (Shader)

有趣的是,Shader 代码本身并不知道它是“动态”的。对 Shader 而言,它只是接收了一个标准的 Uniform Block。

GLSL Vertex Shader (base.vert):

layout (binding = 1) uniform UboInstance { mat4 model; } uboInstance; void main() { // 直接使用 model 矩阵,GPU 会根据动态偏移自动读取正确内存位置 mat4 modelView = uboView.view * uboInstance.model; gl_Position = uboView.projection * modelView * vec4(inPos.xyz, 1.0); // ... }

渲染循环与动态偏移

这是 Dynamic Uniform Buffer 发挥魔力的地方。在绘制循环中,我们遍历所有物体,计算偏移量,并重新绑定描述符集。

// 遍历所有物体实例 for (uint32_t j = 0; j < OBJECT_INSTANCES; j++) { // 计算当前物体的内存偏移量 (索引 * 对齐步长) uint32_t dynamic_offset = j * static_cast<uint32_t>(dynamic_alignment); // 绑定描述符集,并传入 dynamic_offset // 参数 1: set 数量 // 参数 &descriptor_set: 使用同一个 set // 参数 1: dynamic offset 数量 // 参数 &dynamic_offset: 偏移量数组指针 vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 1, &dynamic_offset); // 绘制当前物体 vkCmdDrawIndexed(draw_cmd_buffers[i], index_count, 1, 0, 0, 0); }

注意:虽然这里我们在循环中多次调用了vkCmdBindDescriptorSets,但这比切换不同的VkDescriptorSet对象要轻量得多,因为它复用了同一个句柄,只是改变了内部的指针偏移。


数据更新

在每一帧更新数据时,我们需要利用之前计算的dynamic_alignment进行指针算术,将数据写入 CPU 端的正确位置,然后上传到 GPU。

// 这里的指针运算非常关键 // (uint64_t) 强转是为了按字节偏移 auto model_mat = (glm::mat4 *) (((uint64_t) ubo_data_dynamic.model + (index * dynamic_alignment))); // 更新矩阵数据 *model_mat = glm::translate(glm::mat4(1.0f), pos); // ... 旋转操作 ...

更新完所有数据后,一次性将整个大块内存 flush 到 GPU(如果是 HOST_VISIBLE 内存)。


动态均匀缓冲器

总结

Dynamic Uniform Buffers 是处理大量同类物体渲染的强力工具。

核心要点回顾:

  1. 对齐是关键:必须遵守minUniformBufferOffsetAlignment

  2. 单一描述符:所有物体共用一个VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC类型的 Descriptor Set。

  3. 绘制时绑定偏移:使用vkCmdBindDescriptorSetspDynamicOffsets参数。

通过这种方式,我们在dynamic_uniform_buffers示例中成功高效地渲染了 125 个独立旋转的立方体,既保持了代码的整洁,又优化了 GPU 的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1178403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(77页PPT)DG1145产品质量的源头华为是如何进行需求管理的(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92808859/92565922 资料解读&#xff1a;《&#xff08;77页PPT&#xff09;DG1145产品质量的源头华为是如何进行需求管理的P77》 详细资料请看本解读…

2026国产时序数据库全景图:多模融合破局,企业选型实战指南

2026国产时序数据库全景图&#xff1a;多模融合破局&#xff0c;企业选型实战指南 摘要&#xff1a;进入2026年&#xff0c;在“数字中国”与工业物联网浪潮的强劲推动下&#xff0c;国产时序数据库市场持续繁荣&#xff0c;竞争格局日趋清晰。本文将对当前主流的国产时序数据库…

自监督学习让医疗视频分析准确率翻倍

&#x1f4dd; 博客主页&#xff1a;Jax的CSDN主页 自监督学习&#xff1a;医疗视频分析准确率的革命性跃升目录自监督学习&#xff1a;医疗视频分析准确率的革命性跃升 目录 引言&#xff1a;医疗视频分析的瓶颈与突破 自监督学习的技术内核&#xff1a;从数据饥渴到高效学习 …

收集自己的每日消费类型(餐饮,购物,娱乐),统计每周各类型的消费时长,输出消费结构优化建议

1. 实际应用场景描述场景某职场人希望精细化管理个人财务&#xff0c;每天记录消费类型&#xff08;餐饮、购物、娱乐等&#xff09;、金额及对应的平均每小时消费额&#xff08;例如&#xff1a;餐饮每小时50元&#xff0c;购物每小时100元&#xff09;&#xff0c;从而计算消…

如何借助AI写好论文中的“前人工作”与“现有问题”?用ChatGPT提供全新思路颠覆认知,实测有效,直接使用

我们在写学术论文时,“前人工作”和“现有问题”这两个部分实际上至关重要,前者可以为我们的研究提供夯实的基础,后者则可以借机展示研究价值和创新点。怎么写好这两部分,既要准确反映前人成果,又要凸显研究意义以及创新呢? 七哥经常指导同仁写学术论文,总结出一些借助…

散热效率提升80%!3D VC如何解决AI算力“发热危机”?

&#x1f393;作者简介&#xff1a;科技自媒体优质创作者 &#x1f310;个人主页&#xff1a;莱歌数字-CSDN博客 &#x1f48c;公众号&#xff1a;莱歌数字&#xff08;B站同名&#xff09; &#x1f4f1;个人微信&#xff1a;yanshanYH 211、985硕士&#xff0c;从业16年 从…

leetcode 884. Uncommon Words from Two Sentences 两句话中的不常见单词

Problem: 884. Uncommon Words from Two Sentences 两句话中的不常见单词 解题过程 统计每个单词的频次&#xff0c;1的就是答案 Code class Solution { public:unordered_map<string, int> ump;vector<string> uncommonFromSentences(string s1, string s2) {s1 …

vue.js中如何集成WebUploader实现大文件分片上传源码?

北京码农の10G文件上传奇遇&#xff1a;在胡同里写信创代码 各位好&#xff0c;我是老张&#xff0c;北京中关村某软件公司“脱发攻坚队”队长。最近接了个政府项目&#xff0c;要求上传10G文件&#xff0c;还必须兼容信创环境并提供全套文档——这活儿就像在故宫里装Wi-Fi&am…

SpringAI实践-MCP使用

大多数Agent都不会仅仅满足于只回答问题,它们还需要理解用户意图后,需要做一些真正执行的操作。Agent应用中,调用的大模型相当于Agent的大脑,负责推理,而集成的MCP Server,则相当于真正的“手”做一些执行操作。 …

leetcode 883. Projection Area of 3D Shapes 三维形体投影面积-耗时100

Problem: 883. Projection Area of 3D Shapes 三维形体投影面积 解题过程 耗时100%&#xff0c;xy投影数!0的数量&#xff0c;另外两侧投影拿到行最大值、列最大值&#xff0c;累加 Code class Solution { public:int projectionArea(vector<vector<int>>& gr…

400w微型逆变器, 基于stm32g474实现 设计方案,不是成品 带有源代码、原理图(AD...

400w微型逆变器, 基于stm32g474实现 设计方案&#xff0c;不是成品 带有源代码、原理图(AD)、PCB(AD)一、项目概述 本项目是基于STM32G474微控制器实现的400W微型逆变器控制系统&#xff0c;通过精准的硬件外设配置与软件逻辑设计&#xff0c;实现直流到交流的电能转换&#xf…

基于SpringBoot的粮仓管理系统毕设

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于SpringBoot框架的粮仓管理系统&#xff0c;以满足现代粮仓管理的高效、便捷和智能化需求。具体研究目的如下&#xff1a; 首先&…

也许是集合幂级数

这里会选一些集合幂级数题,但是作者至今不知道集合幂级数是什么东西,所以可能做法不是集合幂级数()。 P13275 好像是区分金银的题? 感觉比省选的岁月简单啊,不知道是不是这个题不是在图上的缘故。 就首先,你考虑…

【后端】【Java】一文详解Spring Boot RESTful 接口统一返回与异常处理实践 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Spring Boot 3 + GraalVM Native Image 原理:从启动 10秒 到 0.05秒,AOT 编译到底干了什么? - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2026年知名的翻译企业,天使翻译公司口碑与实力并存 - 工业品牌热点

2026年全球化商务与跨文化交流持续升温,专业翻译服务已成为企业拓展国际市场、个人处理跨境事务的核心支撑。无论是法律合同的精准转译、国际会议的同传保障,还是音视频内容的本地化适配,优质翻译服务商的专业能力直…

基于SpringBoot的进销存系统毕设

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于SpringBoot框架的进销存系统&#xff0c;以满足现代企业对库存管理、销售管理和采购管理的需求。具体研究目的如下&#xff1a;提高…

SpringAI实践-MCP使用与创建

大多数Agent都不会仅仅满足于只回答问题,它们还需要理解用户意图后,需要做一些真正执行的操作。Agent应用中,调用的大模型相当于Agent的大脑,负责推理,而集成的MCP Server,则相当于真正的“手”做一些执行操作。 …

Python、CSharp、Go、Nextjs,不同框架的性能到底差多少?

前言 不知不觉居然12月了,最近琐事太多,产出并不高,继续整理一下近期的一些笔记吧。 上次我对部署 Django 框架时用的不同服务器进行对比测试,详见:不同Django服务器和部署方式的性能调研 在探索的过程中,我还对…

芯片制造企业如何利用百度WEB编辑器处理PDF技术文档?

河南软件工程大三狗的CMS升级记&#xff1a;从Word粘贴到Latex公式&#xff0c;99元预算的极限操作&#xff01; 一、项目背景&#xff1a;穷学生的倔强 作为一枚即将毕业的大三狗&#xff0c;自己撸了个CMS新闻管理系统&#xff0c;但后台编辑器太挫——从Word复制内容粘贴进…