Общая память вычислительных шейдеров содержит артефакты

Я пытался написать общую реализацию вычислительного шейдера по Гауссу.

Он в основном работает, но содержит артефакты, которые меняют каждый кадр, даже когда сцена статична. Последние несколько часов я пытался отладить это. Я зашел так далеко, что убедился, что границы не превышены, развернул все циклы, заменил униформы константами, но артефакты остались.

Я протестировал исходный код с артефактами на 3 разных машинах / графических процессорах (2 NVIDIA, 1 Intel), и все они дали одинаковые результаты. Моделирование развернутой / постоянной версии выполнения кода с рабочими группами, выполняемыми вперед и назад с помощью простого кода C ++, не вызывает этих ошибок.

введите описание изображения здесь

Выделив совместно используемый массив [96] [96] вместо [16] [48], я могу устранить большинство артефактов.

Это привело меня к мысли, что мне не хватает логической ошибки, поэтому мне удалось создать очень простой шейдер, который по-прежнему выдает ошибку в меньшем масштабе, я был бы признателен, если бы кто-нибудь мог указать причину. Я проверил много документации и не нашел ничего неправильного.

Выделяется общий массив размером 16x48 с плавающей запятой, это 3072 байта, примерно 10% минимального предела общей памяти.

Шейдер запускается в рабочих группах 16x16, поэтому каждый поток будет записывать в 3 уникальных места и читать обратно из одного уникального места.

Затем текстура визуализируется как HSV, при этом значения между 0–1 будут отображаться в оттенок 0–360 (красный-голубой-красный), а значения за пределами границ будут красными.

#version 430
//Execute in 16x16 sized thread blocks
layout(local_size_x=16,local_size_y=16) in;
uniform layout (r32f) restrict writeonly image2D _imageOut;
shared float hoz[16][48];
void main () 
{
    //Init shared memory with a big out of bounds value we can identify
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 20000.0f;
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 20000.0f;
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 20000.0f;
    //Sync shared memory
    memoryBarrierShared();
    //Write the values we want to actually read back
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 0.5f;
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 0.5f;
    hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 0.5f;
    //Sync shared memory
    memoryBarrierShared();
    //i=0,8,16 work
    //i=1-7,9-5,17 don't work (haven't bothered testing further
    const int i = 17;
    imageStore(_imageOut, ivec2(gl_GlobalInvocationID.xy), vec4(hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+i]));
    //Sync shared memory (can't hurt)
    memoryBarrierShared();
}

Запуск этого шейдера с размерами запуска больше 8x8 приводит к появлению артефактов в затронутой области изображения.

glDispatchCompute(9, 9, 0); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

Мне пришлось установить точку останова и пошаговые кадры, чтобы захватить это, потребовалось около 14 кадров < img src = "https://i.stack.imgur.com/M1RaQ.png" alt = "введите описание изображения здесь">

glDispatchCompute(512/16, 512/16, 0);//Full image is 512x512 glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

Мне снова пришлось устанавливать точку останова и шагать кадры, чтобы захватить это, при работе со скоростью 60FPS (vsync) артефакты появлялись чаще / одновременно.

введите описание изображения здесь


person Robadob    schedule 16.12.2016    source источник


Ответы (1)


memoryBarrierShared();

Нет, это делает запись видимой только для других вызовов. Вы должны убедиться, что все записи действительно произошли, если вы хотите иметь возможность читать данные из других вызовов.

Это делается с помощью функции barrier. Который следует называть после memoryBarrierShared.

person Nicol Bolas    schedule 16.12.2016