Vulkan计算着色器中Dispatch、workGroups、invocation之间的关系
在 Vulkan 里,计算着色器 (Compute Shader) 是一种专门用于并行计算的着色器阶段,不参与图形渲染管线。它的执行模型和传统的顶点着色器/片元着色器不同,更接近 GPU 并行计算(GPGPU) 的思想。要理解计算着色器的执行模型,必须搞清楚三个层次的关系:
Dispatch
Workgroups
Invocations
1. Dispatch(任务分发)
在CPU端,你通过调用:vkCmdDispatch(commandBuffer, groupCountX, groupCountY, groupCountZ);来告诉 GPU:我要启动一个大小为 (groupCountX, groupCountY, groupCountZ)
的三维网格。这个网格的每一个格子就是一个 Workgroup。
举例:vkCmdDispatch(cb, 3, 3, 1);表示要启动 3×3×1 = 9
个 Workgroup。可以把 Dispatch 想象成“学校”,它包含多个“班级(workgroup)”。
2. Workgroup(工作组)
Workgroup 是 Dispatch 中的基本单元。
每个 Workgroup 包含若干个 Invocation(线程),它们一起完成数据处理。Workgroup 内线程数(也叫 Local Size),是在着色器代码里定义的:
// 每个 workgroup 有 2×2×1 个线程
layout(local_size_x = 2, local_size_y = 2, local_size_z = 1) in;
每个 Workgroup 内的线程数量是固定的。
所有 Workgroup 的线程布局相同。
继续上面的例子,如果我们 Dispatch(3,3,1)
,那么:
Workgroup 总数 = 9
每个 Workgroup 线程数 = 2×2×1 = 4
总线程数 (Invocation 数量) = 9 × 4 = 36
可以把 Workgroup 想象成“班级”,它里面有多个“学生(invocation)”。
3. Invocation(调用 / 线程)
Invocation 就是 GPU 上真正运行的一个线程。
每个 Invocation 负责处理一小部分数据。
它有三个重要的内置 ID:
局部 ID
:
gl_LocalInvocationID
表示它在 Workgroup 内的坐标,例如
[0,0,0]
、[1,0,0]
。
工作组 ID:
gl_WorkGroupID
表示它所属的 Workgroup 在 Dispatch 网格中的坐标,例如
[2,2,0]
。
全局 ID:
gl_GlobalInvocationID
全局唯一标识某个线程,计算公式是:
gl_GlobalInvocationID = gl_WorkGroupID * local_size + gl_LocalInvocationID
例子:
Dispatch(3,3,1)
local_size = (2,2,1)
那么:
Workgroup
[0,0,0]
内的[0,0,0]
→ Global ID[0,0,0]
Workgroup
[2,2,0]
内的[1,1,0]
→ Global ID[5,5,0]
可以把 Invocation 想象成“学生”,他有班级编号(workgroup ID),也有学号(local ID),两者组合起来就是全校唯一的编号(global ID)。
总结一下:
Dispatch:决定总共有多少个 Workgroup(CPU 下发任务)。
Workgroup:Dispatch 的基本单元,每个 Workgroup 包含一组线程。
Invocation:Workgroup 内的线程,真正执行计算。
可以想象一下学校 班级 学生的关系,加深记忆
Dispatch = 学校
Workgroup = 班级
Invocation = 学生
而 GPU 在执行时,会同时让大量“学生”一起行动,每个人负责一小块任务。