GLSL, семафоры?
У меня ранее уже была проблема, что я хотел смешать значения цвета в единицу изображения, делая что-то вроде:
vec4 texelCol = imageLoad(myImage, myTexel);
imageStore(myImage, myTexel, texelCol+newCol);
В сценарии, где несколько фрагментов могут иметь одинаковое значение для "myTexel", это, по-видимому, невозможно, поскольку невозможно создать атомарность между командами imageLoad и imageStore, а другие вызовы шейдеров могут изменить цвет текселя между ними.
Теперь кто-то сказал мне, что poeple обходит эту проблему, создавая семафоры с использованием атомарных команд на текстурах uint, так что шейдер будет как-то ждать в цикле while, прежде чем получить доступ к texel и, как только он будет свободен, атомарно записать его как целое число. текстуру, чтобы блокировать другие вызовы фрагмента шейдера, обрабатывать цветной тексель и по окончании атомарно освободить целочисленный тексель снова.
Но я не могу понять, как это может на самом деле работать и как будет выглядеть такой код?
Реально ли это сделать? можно ли установить фрагментный шейдер GLSL для ожидания в цикле while? Если это возможно, кто-нибудь может привести пример?
1 ответ
Да, вы можете сделать это, но только до тех пор, пока порядок, в котором смешиваются вещи, не имеет значения. В противном случае вам нужно использовать обычные методы.
По сути, вы просто реализуете спинлок. Только вместо одной переменной блокировки у вас есть целые значения блокировок текстуры.
Сначала вам нужно изображение размером с другое ваше изображение, которое вы будете использовать для атомарных операций. Это должна быть целочисленная текстура с GL_R32UI
и uimage
формат r32ui
чтобы соответствовать. Это должно быть инициализировано с 0. И атомное изображение, и "смешанное" изображение должны быть объявлены "связными".
Выполнить
imageAtomicCompSwap
операция в месте на атомном целочисленном изображении. Вы сравниваете его с 0, и значение, которое вы устанавливаете, равно 1.Если #1 возвращает 1, это означает, что кто-то еще имеет блокировку. Вернитесь к шагу № 1.
Если #1 возвращает 0, то теперь у вас есть эксклюзивный доступ к текселю (потому что теперь у текселя есть 1, благодаря операции сравнения / замены). Продолжить.
Выполните операцию смешивания. Испускать
memoryBarrier
после вашей операции смешивания.Выполнить
imageAtomicExchange
, со значением 0. Это разблокирует спин-блокировку.
Причина, по которой это работает, связана с атомистикой. GLSL обеспечивает атомарность атома. imageAtomicCompareSwap
выполняет операцию чтения / условного изменения / записи в виде атомарной последовательности. А поскольку он атомарный, другие шейдерные вызовы не могут предвосхищать или прерывать операцию. Это означает, что не имеет значения, сколько потоков шейдера запущено: если 100 из них вызывают imageAtomicCompareSwap(..., 0, 1)
точно один из них получит 1 в качестве возвращаемого значения, а остальные получат 0 (для того же texel, конечно).
Таким образом, только один поток получит блокировку; остальное должно ждать.
Использование memoryBarrier()
Функция гарантирует, что другие ожидающие потоки получат измененные данные, когда перейдут на их чтение. Опять же, вам нужно использовать coherent
квалификатор для изображения, с которым вы делаете это.