3.5 推常量与资源索引化


文档摘要

3.5 推常量与资源索引化 本节摘要:给着色器递参数有三条路:描述符集走柜台、推常量走直通窗口、描述符索引化把整排资源压缩成一段可下标的数组。本节讲清三条路的容量边界、性能特征与工程适用,帮你按数据规模与更新频率做正确选择。 「Push constant」这个行话常被直译成推常量,更传神的叫法是「直通窗口」:不办集合、不占描述符配额,命令里直接夹带一小段数据,着色器当作常量读取。它是描述符系统(3.4 节)的补充通道,而描述符索引化则是对描述符本身的压缩改造。本节把三条递参路线放在一起盘清。 一、推常量:小而快的一手交割 推常量的使用分两步:在管线布局里申报容量与着色阶段,然后在命令缓冲里推数据。 着色器侧声明对应即可:GLSL 里写 。三条边界要背下来。

3.5 推常量与资源索引化

本节摘要:给着色器递参数有三条路:描述符集走柜台、推常量走直通窗口、描述符索引化把整排资源压缩成一段可下标的数组。本节讲清三条路的容量边界、性能特征与工程适用,帮你按数据规模与更新频率做正确选择。

「Push constant」这个行话常被直译成推常量,更传神的叫法是「直通窗口」:不办集合、不占描述符配额,命令里直接夹带一小段数据,着色器当作常量读取。它是描述符系统(3.4 节)的补充通道,而描述符索引化则是对描述符本身的压缩改造。本节把三条递参路线放在一起盘清。

一、推常量:小而快的一手交割

推常量的使用分两步:在管线布局里申报容量与着色阶段,然后在命令缓冲里推数据。

// 组装管线布局时申报:128 字节,顶点与片段可见 VkPushConstantRange range = {}; range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; range.offset = 0; range.size = 128; VkPipelineLayoutCreateInfo plci = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; plci.setLayoutCount = 1; // 3.4 的集合布局 plci.pSetLayouts = &setLayout; plci.pushConstantRangeCount = 1; plci.pPushConstantRanges = ⦥ vkCreatePipelineLayout(device, &plci, NULL, &pipelineLayout); // 录制命令时直接推送 struct Params { glm::mat4 mvp; float time; } params = { mvp, t }; vkCmdPushConstants(cmd, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Params), &params);

着色器侧声明对应即可:GLSL 里写 layout(push_constant) uniform Params { mat4 mvp; float time; };。三条边界要背下来。容量:规范只保证每阶段至少 128 字节,真实上限用 maxPushConstantsSize 查询(常见值 256 字节),超了就是非法。阶段归属:推送时声明的阶段集合必须落在申报的 range 之内,顶点推的数据片段看不见,除非申报时含片段阶段。生命周期:数据值在命令录制时快照进命令缓冲——推完就改源变量不影响已录制命令,这既是优点(天然无竞态)也是约束(每帧每对象都要重录或重推)。

适用判断一句话:少于一百来字节、每次绘制都不同的量(mvp、时间、物体编号),推常量是最短路径;更大的结构或需要着色器写入的,回柜台走描述符。

二、三条递参路线对比

把推常量放进完整货架里比价:

路线 容量 更新成本 适用 限制
推常量 数百字节内 录制时夹带,近零 mvp、小参数包 只读、容量小、随命令重录
uniform 描述符 数 KB 级 填册加在途分册(3.4) 材质参数、光照数据 对齐要求、更新时序
storage 描述符 数十 MB 级 同上,可读写 粒子、蒙皮、大表 布局松散、带宽换容量
动态偏移 复用单缓冲 绑定时换偏移 每对象常量的批量版 偏移对齐
描述符索引化 近乎无限资源数 绑一次用全场 海量纹理与材质 需扩展特性、着色器需下标

三、描述符索引化:把点名册压成数组

传统描述符每次换资源都要换册重绑,海量的做法是把几十上百张纹理的描述符填进同一个集合的数组里,着色器按下标取用——业内俗称 bindless 思路。Vulkan 的对应能力是描述符索引化,要点是两个特性开关:updateAfterBind(绑进命令缓冲后仍可更新数组内容)与 nonUniformIndexing(下标不是统一表达式时也能正确寻址,例如每像素按材质 ID 取纹理)。

// 布局声明:一排 4096 张组合采样器,片段可见,允许绑定后更新 VkDescriptorSetLayoutBinding b = {}; b.binding = 0; b.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; b.descriptorCount = 4096; b.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorBindingFlags bindFlag = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; // 允许数组只填一部分 VkDescriptorSetLayoutBindingFlagsCreateInfo flags = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO}; flags.bindingCount = 1; flags.pBindingFlags = &bindFlag; VkDescriptorSetLayoutCreateInfo lci = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; lci.pNext = &flags; lci.bindingCount = 1; lci.pBindings = &b; lci.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; // 着色器侧:layout(set=0, binding=0) uniform sampler2D textures[4096]; // 取用:texture(textures[materialId], uv);

PARTIALLY_BOUND 位很实用:数组里只有实际用到的槽位需要有效描述符,没填的槽位只要不被下标到就合法——注册表初始化从此不用「先灌满四千张」。代价也有三条:两个特性都要查询支持(老设备可能只有其一);数组容量受 maxPerStageDescriptorSamplers 等上限约束(照例查询);着色器调试时「下标到无效槽」的行为未定义,需要自己的哨兵纹理兜底。

四、案例:渲染器递参路线的演进

背景:一个自研渲染器初版给每个材质配一套描述符集,场景一多(两千多种材质组合),绑定调用与集合分配都成了 CPU 瓶颈,帧前处理耗时中「换册」占了近一半。

操作:按数据特征分层改造。每对象必变的 mvp 迁到推常量(数十字节,绘制时直推);材质的标量参数合并进一条大 uniform 缓冲,用动态偏移按材质选槽;纹理数组化——全部 albedo 与 normal 贴图注册进两个索引化数组,材质记录里只存下标,着色器按下标采样。绑定粒度从「每对象两册」降到「每帧数册」。

结果:换册开销近乎清零,CPU 帧时间下降明显,场景规模上限提升一个数量级。内存侧多付出的是注册表描述符的容量(数 MB 级)与哨兵纹理。

解读:三条路线的选择依据是「数据在帧内的变化频率」——每绘制必变的走直通、每帧稳定的走偏移、整场不变的走索引。频率与通道匹配后,绑定次数自然降到最低;反过来,「把 mvp 也塞进索引数组」这类错配虽然能跑,却把简单问题复杂化。

变式:计算管线(第 5 章)里同样的分层照样成立:分派常量(组尺寸、参数)走推常量,中间结果走 storage 缓冲,只读大表走索引化数组。本章的递参框架是跨管线通用的。

五、要点回顾

  • 直通窗口:推常量免集合免配额,容量按查询(保底 128 字节),值在录制时快照,天然无更新竞态。
  • 路线按频率选:每绘制变走推常量、每帧稳定走动态偏移、全场不变走索引化,错配能跑但吃亏。
  • 索引化两开关:updateAfterBind 与 nonUniformIndexing 都要查询支持,PARTIALLY_BOUND 免全量初始化。
  • 上限照例查询:描述符数组容量受各阶段上限约束,哨兵资源兜底无效下标。
  • 分层组合:真实渲染器同时使用全部路线,按数据变化频率分层安置是设计重心。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U