GLSL, семафоры?

У меня уже была проблема, что я хотел смешать значения цвета в единице изображения, выполнив что-то вроде:

vec4 texelCol = imageLoad(myImage, myTexel);
imageStore(myImage, myTexel, texelCol+newCol);

В сценарии, где несколько фрагментов могут иметь одно и то же значение для 'myTexel', это не возможно, потому что нельзя создавать атомарность между командами imageLoad и imageStore, а другие shaderinvocations могут изменять цвет текселя между ними.

Теперь кто-то сказал мне, что poeple работают вокруг этой проблемы, создавая семафоры, используя атомарные комманды на текстурах uint, так что шейдер будет как-то ждать в цикле до доступа к текселю, и как только он станет свободным, атомарно напишите itno целочисленная текстура, чтобы блокировать другие вызовы фрагментарного шейдера, обрабатывать цветной тексель и, когда он закончил атомарно освобождать целочисленный тексель снова.

Но я не могу понять, как это могло бы работать и как выглядел бы такой код?

Действительно ли это возможно? может ли шейдер фрагмента GLSL быть установленным для ожидания в цикле while? Если это возможно, может ли кто-нибудь привести пример?

Ответ 1

По сути, вы просто реализуете спинлок. Только вместо одной переменной блокировки у вас есть целая текстура блокировок.

Логично, что вы делаете, имеет смысл. Но что касается OpenGL, на самом деле это не сработает.

Смотрите, модель выполнения шейдера OpenGL утверждает, что вызовы выполняются в порядке, который в значительной степени не определен по отношению друг к другу. Но спин-блокировки работают только в том случае, если есть гарантия продвижения вперед между различными потоками. По сути, спин-блокировки требуют, чтобы поток, который вращается, не мог заставить исполнительную систему не запускать поток, который он ожидает.

OpenGL не предоставляет такой гарантии. Это означает, что один поток может полностью заблокировать пиксель, а затем прекратить выполнение (по какой-либо причине), в то время как другой поток блокирует этот пиксель. Блокированный поток никогда не прекращает выполнение, а поток, которому принадлежит блокировка, никогда не возобновляет выполнение.

Как это может произойти в реальной системе? Хорошо, допустим, у вас есть группа вызова фрагмента шейдера, выполняющаяся на некоторых фрагментах из треугольника. Все они блокируют свои пиксели. Но тогда они расходятся в исполнении из-за условного перехода в пределах области блокировки. Расхождение в исполнении может означать, что некоторые из этих вызовов передаются в другой исполнительный блок. Если в данный момент нет ни одного доступного, тогда они фактически останавливаются, пока один не станет доступным.

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

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