Вычислить шейдер - gl_GlobalInvocationID и local_size

При попытке реализовать наивный вычислительный шейдер, который назначает кластеру влияющие источники света, я столкнулся с неожиданным (хорошо для нуба, как я) поведением:

Я вызываю этот шейдер с помощью glDispatchCompute(32, 32, 32); и он должен записывать [легкий счетчик + 8 индексов] для каждого вызова в буфер "индексы". Но во время отладки я обнаружил, что мои записи в этот буфер перекрываются между вызовами, хотя я использую уникальный clusterId. Я обнаруживаю это по значениям индексов [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++;
        }
    }
}

1 ответ

Решение

Давайте посмотрим на две декларации:

layout (local_size_x = 4, local_size_y = 4, local_size_z = 4) in;

а также

const unsigned int clusterSize = 32;

Они говорят разные вещи. local_size В объявлении говорится, что у каждой рабочей группы будет 4 * 4 * 4 вызова, что составляет 64. clusterSize говорит, что каждая рабочая группа будет иметь только 32 вызова.

Если вы хотите решить эту проблему, используйте фактическую константу локального размера, предоставленную системой:

const unsigned int clusterSize = dot(gl_WorkGroupSize, uvec3(1, 1, 1));

И вы даже можете сделать это:

const uvec3 linearizeInvocation = uvec3{1, clusterSize, clusterSize * clusterSize};

...

unsigned int clusterId = dot(gl_GlobalInvocationID, linearizeInvocation);
Другие вопросы по тегам