计算着色器 - gl_GlobalInvocationID 和 local_size
Compute Shader - gl_GlobalInvocationID and local_size
在尝试实现将影响灯光分配给集群的朴素计算着色器时,我遇到了意外(对于像我这样的新手来说)的行为:
我用 glDispatchCompute(32, 32, 32) 调用这个着色器;并且它应该为每次调用写入一个 [light counter + 8 indices] 到 "indices" 缓冲区。但是在调试时,我发现 我写入该缓冲区的操作在调用之间重叠 ,即使我使用唯一的 clusterId。我通过 indices[outIndexStart] 的值超过 8 和视觉闪烁来检测它。
根据文档,gl_GlobalInvocationID 是 gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID。但是,如果将所有局部大小设置为 1,写入问题就会消失。为什么 local_size 会以这种方式影响这段代码?我怎么能在这里选择它的价值呢?
#version 430
layout (local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
uniform int lightCount;
const unsigned int clusterSize = 32;
const unsigned int clusterSquared = clusterSize * clusterSize;
struct LightInfo {
vec4 color;
vec3 position;
float radius;
};
layout(std430, binding = 0) buffer occupancyGrid {
int exists[];
};
layout(std430, binding = 2) buffer lightInfos
{
LightInfo lights [];
};
layout(std430, binding = 1) buffer outputList {
int indices[];
};
void main(){
unsigned int clusterId = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * clusterSize + gl_GlobalInvocationID.z * clusterSquared;
if(exists[clusterId] == 0)
return;
//... not so relevant calculations
unsigned int outIndexStart = clusterId * 9;
unsigned int outOffset = 1;
for(int i = 0; i < lightCount && outOffset < 9; i++){
if(distance(lights[i].position, wordSpace.xyz) < lights[i].radius) {
indices[outIndexStart + outOffset] = i;
indices[outIndexStart]++;
outOffset++;
}
}
}
我们来看两个声明:
layout (local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
和
const unsigned int clusterSize = 32;
这些表示不同的事情。 local_size
声明表示每个工作组将有 4x4x4 次调用,即 64 次。相比之下,您的 clusterSize
表示每个工作组将只有 32 次调用。
如果要解决此问题,请使用 actual local size constant provided by the system:
const unsigned int clusterSize = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z;
你甚至可以这样做:
const uvec3 linearizeInvocation = uvec3{1, clusterSize, clusterSize * clusterSize};
...
unsigned int clusterId = dot(gl_GlobalInvocationID, linearizeInvocation);
在尝试实现将影响灯光分配给集群的朴素计算着色器时,我遇到了意外(对于像我这样的新手来说)的行为:
我用 glDispatchCompute(32, 32, 32) 调用这个着色器;并且它应该为每次调用写入一个 [light counter + 8 indices] 到 "indices" 缓冲区。但是在调试时,我发现 我写入该缓冲区的操作在调用之间重叠 ,即使我使用唯一的 clusterId。我通过 indices[outIndexStart] 的值超过 8 和视觉闪烁来检测它。
根据文档,gl_GlobalInvocationID 是 gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID。但是,如果将所有局部大小设置为 1,写入问题就会消失。为什么 local_size 会以这种方式影响这段代码?我怎么能在这里选择它的价值呢?
#version 430
layout (local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
uniform int lightCount;
const unsigned int clusterSize = 32;
const unsigned int clusterSquared = clusterSize * clusterSize;
struct LightInfo {
vec4 color;
vec3 position;
float radius;
};
layout(std430, binding = 0) buffer occupancyGrid {
int exists[];
};
layout(std430, binding = 2) buffer lightInfos
{
LightInfo lights [];
};
layout(std430, binding = 1) buffer outputList {
int indices[];
};
void main(){
unsigned int clusterId = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * clusterSize + gl_GlobalInvocationID.z * clusterSquared;
if(exists[clusterId] == 0)
return;
//... not so relevant calculations
unsigned int outIndexStart = clusterId * 9;
unsigned int outOffset = 1;
for(int i = 0; i < lightCount && outOffset < 9; i++){
if(distance(lights[i].position, wordSpace.xyz) < lights[i].radius) {
indices[outIndexStart + outOffset] = i;
indices[outIndexStart]++;
outOffset++;
}
}
}
我们来看两个声明:
layout (local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
和
const unsigned int clusterSize = 32;
这些表示不同的事情。 local_size
声明表示每个工作组将有 4x4x4 次调用,即 64 次。相比之下,您的 clusterSize
表示每个工作组将只有 32 次调用。
如果要解决此问题,请使用 actual local size constant provided by the system:
const unsigned int clusterSize = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z;
你甚至可以这样做:
const uvec3 linearizeInvocation = uvec3{1, clusterSize, clusterSize * clusterSize};
...
unsigned int clusterId = dot(gl_GlobalInvocationID, linearizeInvocation);