WebGPU 的 primitive_index 内置变量:从提案到 WGSL 规范的逐片元索引实战指南
图形学【免费下载链接】gpuwebWhere the GPU for the Web work happens!项目地址https://gitcode.com/gh_mirrors/gp/gpuweb点击查看免费下载本文基于 gpuweb 仓库中的 primitive-index 提案文档 及其在 WebGPU/WGSL 规范中的落地实现系统讲解primitive_index这一片元fragment着色器内置变量的语义、原生 API 对照、WGSL 启用方式与实战用法。读完本文你将掌握如何在 WGSL 中声明并消费primitive_index、如何通过GPUDevice的requestDevice特性开关启用primitive-index功能以及该索引在不同图元拓扑下的精确计数规则。一、什么是 primitive_indexprimitive_index是 WebGPU 引入的一个 WGSL 内置输入变量为单次绘制实例draw instance内的每一个图元三角形、线段或点提供一个唯一的整数编号。它之于片元着色器类似于vertex_index之于顶点着色器、instance_index之于实例它在片元着色器中可读类型为u32它在当前绘制操作内从 0 开始每处理完一个点、线或三角形图元即自增 1它在每两个实例instance之间重置为 0。这一能力在底层原生图形 APID3D、Metal、Vulkan、OpenGL中早已是常见特性因此在 WebGPU 中支持它能显著提升现有着色器直接移植到 Web 的可行性。典型应用场景扁平着色flat shading用图元索引驱动每个图元的统一着色实现每个三角形一个颜色的经典可视化效果高级材质效果为每个图元分配独立的伪随机种子或材质 ID避免顶点级数据插值带来的歧义虚拟化几何管线现代大规模场景管理如几何裁剪、LOD 切换、GPU 驱动的实例化需要精确知道当前正在处理第几个图元以便从结构化缓冲中检索对应元数据。二、提案状态已合并进规范该提案创建于 2025-09-03对应的 GitHub Issue 为 #1786。根据 proposals/README.md 中的状态分类primitive-index已归入Status: Merged——即该提案已经过工作组批准正式加入 WebGPU 与 WGSL 规范。仓库保留该文档作为解释性说明与历史存档因此对于行为细节的最终裁决应始终以规范为准。这一点在规范源码中有直接证据spec/index.bs 的GPUFeatureName枚举中已加入字符串字面量primitive-indexspec/index.bs 给出了该 feature 的正式定义启用后允许在 WGSL 中使用primitive_index并引入对应的 WGSL 扩展primitive_index。三、原生 API 可用性对照提案文档给出了各主流原生 API 中对应能力的一览表这也是该内置变量之所以需要特性门控的原因——并非所有平台都无条件提供平台类型说明SPIR-V32 位整数需要几何着色器、网格着色器或光线追踪以内建量PrimitiveID提供HLSLu32需要 D3D10以语义SV_PrimitiveID提供GLSLi32需要 GLSL 1.50 及以上、ESSL 3.2ESSL 3.1 需配合GL_EXT_geometry_shader以内建量gl_primitiveID提供Metalu32macOS 需 Metal 2.2iOS 需 Metal 2.3以属性[[primitive_id]]提供正因为这种非普适可用性WebGPU 将该能力封装为可选的primitive-index特性并要求在使用时于 WGSL 中显式书写enable primitive_index语句。四、行为语义详解提案文档对primitive_index的运行时行为给出了精确约束这些语义同样被 WGSL 规范采纳。逐条解读如下作用域受限primitive_index仅可在fragment片元着色器中使用不能出现在顶点或计算着色器。起始值与步长第一个图元的索引为 0之后每个后续图元递增 1。实例间重置每绘制一个实例后索引归零即索引计数独立于实例 ID。图元内均匀同一个图元的所有片元读到相同的primitive_index值该值在图元范围内是 uniform 的这使其天然适合驱动逐图元而非逐片元的着色逻辑。与 primitive restart 无关图元重启primitive restart不会改变primitive_index变量的值计数在条带strip重启后仍按顺序延续。溢出语义不适用HLSL 规定图元 ID 超过 2³²−1 时回绕为 0但 WebGPU 所有适用的绘制调用计数参数都是无符号 32 位整数不可能超过该上限因此该回绕规则对 WebGPU 不适用。相邻图元不支持自动生成WebGPU 没有为相邻图元adjacent primitives自动生成索引的能力对于相邻图元索引仅为内部非相邻图元维护。作为佐证wgsl/index.bs 中primitive_index内置值的规范条目写道基于当前绘制操作开始以来、当前实例所处理图元数量的逐图元索引。从 0 开始每处理完一个点、线或三角形图元即自增 1在每个实例之间重置为 0。使用图元重启值重启条带图元不会影响图元索引。该索引在图元的所有片元间保持一致。五、拓扑支持范围GPUPrimitiveTopology中的全部拓扑类型都受支持。一般而言相邻拓扑adjacency topologies不会得到支持但 WebGPU 本身就没有相邻拓扑因此不存在该限制。具体到每种拓扑一个图元的划分如下拓扑图元划分point-list每个顶点即一个图元line-list每对顶点为一个图元line-strip每对相邻顶点为一个图元triangle-list每三个顶点为一个图元triangle-strip每 3 个相邻顶点组成一个图元这也解释了primitive_index的计数粒度在triangle-list下它按每 3 个顶点一计递增在triangle-strip下则按每滑过一个顶点产生一个新三角形递增。六、WGSL 规范中的落地方式从源码层面看primitive_index的加入涉及 WGSL 规范的四处关键改动1. 内置值名称表新增条目wgsl/index.bsbuiltin_value_name语法允许的标识符列表中加入primitive_index同时扩展名称列表也加入primitive_indexwgsl/index.bs。2. 内置输入/输出值表新增一行wgsl/index.bs各字段如下字段值Nameprimitive_indexStagefragmentDirectioninputTypeu32Extensionprimitive_index3. 扩展定义wgsl/index.bs定义 WGSL 扩展primitive_index对应GPUFeatureName/primitive-index。其语义为当该扩展启用时WGSL 模块中使用primitive_index内置变量合法否则任何使用都会导致 shader-creation error着色器创建错误。4. 特性门控WebGPU 侧的primitive-index特性启用后允许在 WGSL 中使用primitive_index见 spec/index.bs。七、实战示例1. 在 GPUDevice 上启用 primitive-index 特性启用过程遵循 WebGPU 标准特性协商模式先检查adapter.features是否包含该特性再通过requestDevice传入requestedFeaturesconst adapter await navigator.gpu.requestAdapter(); const requestedFeatures []; if (adapter.features.has(primitive-index)) { requestedFeatures.push(primitive-index); } else { // 使用替代代码路径或向用户反馈错误。 } const device await adapter.requestDevice({ requestedFeatures });需要说明如果请求了适配器不支持的特性requestDevice会直接拒绝Promise reject因此上文这种先检测、后请求的防御式写法是推荐做法。若设备不支持该特性应走备用路径例如退回基于vertex_index的近似方案其局限见下文备选方案一节。2. 在 WGSL 着色器中使用 primitive_index使用时必须在模块顶部书写enable primitive_index扩展声明否则会产生着色器创建错误enable primitive_index fragment fn fs_main(builtin(primitive_index) prim_index: u32) - location(0) vec4f { return vec4f(f32(prim_index)); }该示例将每个图元的索引转换为灰度颜色输出prim_index 0的图元显示为黑色索引越大颜色越亮适合快速验证索引计数是否符合预期。注意prim_index的类型必须是u32与规范的 Type 字段一致如需参与浮点运算应显式做f32()转换。3. 与顶点/实例索引的协同primitive_index常与以下两个内置变量配合使用vertex_indexwgsl/index.bs当前顶点在绘制命令内的索引仅限 vertex 着色器非索引绘制时等于firstVertex参数索引绘制时等于索引缓冲条目值加上baseVertex。instance_indexwgsl/index.bs当前顶点所属实例的索引仅限 vertex 着色器第一个实例的索引等于绘制命令的firstInstance参数。典型组合场景顶点着色器用vertex_index采样每个顶点的几何属性用instance_index采样实例级数据片元着色器用primitive_index为每个图元检索独立的材质或随机种子。三者在各自阶段各司其职共同支撑 GPU 驱动的虚拟化几何渲染。八、备选方案与设计取舍提案文档明确指出primitive_index提供的值在所有场景下都无法仅靠 API 之外的手段完全模拟。最务实的替代思路是从已有的vertex_index内置变量推导图元索引但这一方案不支持索引几何indexed geometry——因为顶点索引在索引缓冲中的顺序与图元装配顺序并不对应无法可靠地反推出这是第几个图元。而索引几何恰恰是大型网格最常用的格式能显著降低内存占用。因此该限制不可接受最终需要专用的primitive_index内置变量。从原生 API 对照表可以看出各平台对图元 ID 的呈现方式类型、阶段、扩展门槛并不一致这正是 WebGPU 将其封装为可选特性并配套 WGSL 扩展门控的根本原因——让 Web 开发者获得统一、可预测的跨平台语义。九、总结primitive_index是 WebGPU 片元着色器的u32类型内置输入为单次绘制的每个图元提供从 0 开始的递增唯一编号实例间重置。该特性已完成提案阶段并合并入 WebGPU/WGSL 规范仓库中的 primitive-index 提案 保留为历史存档行为以 wgsl/index.bs 与 spec/index.bs 为准。使用前需同时满足两个前置条件在GPUDevice上启用primitive-index特性并在 WGSL 模块顶部声明enable primitive_index否则产生着色器创建错误。它支持全部GPUPrimitiveTopology拓扑不受图元重启影响在图元内保持 uniform且无需担心 HLSL 式的 32 位回绕问题。对于大型网格的虚拟化渲染、逐图元材质与扁平着色等效果它是不可替代的底层能力。赞分享图形学【免费下载链接】gpuwebWhere the GPU for the Web work happens!项目地址https://gitcode.com/gh_mirrors/gp/gpuweb点击查看免费下载相关推荐从SkSL到WGSL解锁WebGPU渲染新范式从SkSL到WGSL解锁WebGPU渲染新范式 你是否在WebGPU项目中为着色器移植头疼是否因SkSL与WGSL语法差异而反复调试本文将带你掌握Skia图形学gpuweb 仓库贡献实战WebGPU 与 WGSL 规范的本地构建、验证与预览指南gpuweb 仓库贡献实战WebGPU 与 WGSL 规范的本地构建、验证与预览指南 本文是一份面向 gpuweb 仓库贡献者的技术操作指南。gpuweb 是图形学WebGPU GPUBuffer 内存映射完全指南从 BufferOperations 设计草案到 mapAsync 规范实现WebGPU GPUBuffer 内存映射完全指南从 BufferOperations 设计草案到 mapAsync 规范实现 GPU 与 Web 之间的数据图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考