Как именно «связный» классификатор памяти GLSL интерпретируется драйверами GPU для многопроходного рендеринга? - PullRequest
4 голосов
/ 16 марта 2012

В спецификации GLSL для «связного» квалификатора памяти указано: «переменная памяти, в которой операции чтения и записи согласованы с операциями чтения и записи из других вызовов шейдеров» .

На практике я не уверен, как это интерпретируется современными драйверами графического процессора в отношении нескольких проходов рендеринга. Когда в спецификации GLSL указано «другие вызовы шейдеров», относится ли это к вызовам шейдеров, выполняющимся только во время текущего прохода, или к любым возможным вызовам шейдеров в прошлых или будущих проходах? Для моих целей я определяю проход как цикл "glBindFramebuffer-glViewPort-glUseProgram-glDrawABC-glDrawXYZ-glDraw123" цикл; где я в настоящее время выполняю 2 таких прохода на «итерацию цикла рендеринга», но может иметь больше на каждую итерацию позже.

1 Ответ

9 голосов
/ 16 марта 2012

Когда в спецификации GLSL указано «другие вызовы шейдеров», относится ли это к вызовам шейдеров, выполняющимся только во время текущего прохода, или к любым возможным вызовам шейдеров в прошлых или будущих проходах?

Это означает именно то, что написано: "другие вызовы шейдеров". Это может быть тот же программный код. Это может быть другой код. Это не имеет значения: вызовы шейдеров, которые не являются такими.

Обычно OpenGL обрабатывает синхронизацию за вас, потому что OpenGL может довольно легко это отслеживать. Если вы отобразите диапазон объекта буфера, измените его и удалите его, OpenGL знает, сколько вещей вы (потенциально) изменили. Если вы используете glTexSubImage2D, он знает, сколько вещей вы изменили. Если вы преобразуете обратную связь, она может точно знать, сколько данных было записано в буфер.

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

Когда вы имеете дело с загрузкой / хранением изображений, вы теряете все это . Поскольку так много можно было бы написать совершенно случайным, неизвестным и непостижимым образом, OpenGL, как правило, не соблюдает правила, чтобы вы могли гибко получать максимально возможную производительность. Это вызывает много неопределенного поведения.

В общем, единственные правила, которые вы можете соблюдать, изложены в разделе 2.11.13 спецификации OpenGL 4.2. Самым большим (для разговоров шейдеров в шейдерах) является правило на этапах. Если вы находитесь в фрагментном шейдере, можно с уверенностью предположить, что вершинный шейдер (-ы), который специально вычислил точку / линию / треугольник для вашего треугольника , завершены. Следовательно, вы можете свободно загружать значения, которые были ими сохранены. Но только из тех, которые сделали вас .

Ваши шейдеры не могут делать предположения о том, что шейдеры, выполненные в предыдущих командах рендеринга, были выполнены (я знаю, что это звучит странно, учитывая то, что только что было сказано, но помните: «только из тех, которые сделали вас»). Ваши шейдеры не могут делать предположения о том, что другие вызовы того же шейдера, использующие ту же форму, изображения, текстуры и т. Д., В одной и той же команде рендеринга завершены, за исключением случаев, описанных выше.

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

Ну, если кто-то еще не написал ему в это время.

Ваши шейдеры не могут предположить, что более поздняя команда рендеринга определенно извлечет значения, записанные (через хранилище изображений или атомарные обновления) предыдущей. Неважно, сколько позже! Неважно, что вы привязали к контексту. Не имеет значения, что вы загрузили или загрузили (технически. Скорее всего, вы получите правильное поведение в некоторых случаях, но неопределенное поведение по-прежнему undefined ).

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

Следовательно, если вы рендерите что-то, что сохраняет изображение, вы не сможете рендерить что-то, использующее сохраненные данные, пока не будет отправлен соответствующий барьер (явно в шейдер или явно в код OpenGL). Это может быть операция загрузки изображения. Но это может быть рендеринг из объекта буфера, написанного кодом шейдера. Это может быть выборка текстуры. Это может быть смешивание с изображением, прикрепленным к FBO. Это не имеет значения; ты не можешь сделать это.

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

...