Вулкан барьеры и многопоточность
Я хочу поделиться своими мыслями о том, как синхронизировать барьеры памяти при многопоточном рендеринге. Пожалуйста, дайте мне знать, если мои мысли о барьере памяти Вулкана неверны или мой текущий план имеет какой-то смысл. У меня нет никого на работе для обсуждения, поэтому я попрошу здесь о помощи.
Для ресурсов в Vulkan, когда я устанавливаю для них барьеры памяти между вызовами, мне нужно установить srcAccessMask и dst AccessMask. Это просто для однопоточного рендеринга. Но для многопоточного рендеринга это усложняется. dst AccessMask не является проблемой, так как мы всегда знаем, для чего будет использоваться ресурс. Но для srcAccessMask, когда один командный буфер пытается прочитать текущую маску доступа какого-либо ресурса, могут быть другие командные буферы, изменяющие его на что-то другое. Итак, мои нынешние мысли о его решении:
Каждый ресурс сохраняет свое собственное состояние, я буду обновлять его только перед отправкой буферов команд в очередь команд, я опишу его позже. В каждом буфере команд ведется запись о том, как внутри него изменилось состояние ресурса. Таким образом, в одном и том же буфере команд состояние доступа каждого ресурса ясно, единственной проблемой является начальное состояние ресурса для каждого буфера команд.
При отправке нескольких командных буферов для выполнения, поскольку порядок командных буферов теперь фиксирован, я проверяю запись отслеживания каждого ресурса среди всех командных буферов, обновляю состояние ресурса на основе конечного состояния ресурса в каждом командном буфере и использую это исправить начальное состояние одного и того же ресурса в записи отслеживания каждого буфера команд.
Затем мне нужно либо вставить новый буфер команд, чтобы иметь дополнительный барьер памяти для ресурса перехода для исправления состояния для первого буфера команд, либо вставить барьер памяти в буфер предыдущих команд для буферов остальных команд. Когда все это будет сделано, я наконец смогу отправить командные буферы вместе как пакет.
Это имеет смысл для вас? Есть ли лучшие решения для ее решения? Или нам даже нужно решить проблему "синхронизации" состояния доступа для каждого ресурса?
Спасибо за ваше время
1 ответ
То, о чем вы говорите, имеет смысл только в мире, где ни одна из этих операций рендеринга не имеет ни малейшего представления о том, что происходит в другом месте. Там, где потребитель изображения не имеет представления о том, как туда попали данные на изображении. Что, вероятно, означает, что он на самом деле не знает, что означает это изображение концептуально.
Vulkan - это API низкого уровня. Идея состоит в том, что вы можете подключить концепции высокого уровня вашей системы рендеринга непосредственно к Vulkan. Таким образом, на высоком уровне вы знаете, что ресурс X имеет значение Y, и в этом кадре его данные будут сгенерированы из операции Z. Не из-за чего-то, хранящегося в ресурсе X, а потому, что это ресурс X; для этого и нужен ресурс X Таким образом, и операция, которая ее генерирует, и операция, которая ее потребляет, знают, что происходит и как она туда попала.
Например, если вы выполняете отложенный рендеринг и SSAO, тогда ваш SSAO renderpass знает, что текстура, содержащая буфер глубины, имеет свои значения, сгенерированные рендерингом. Буферу глубины не нужно что-то хранить в нем, чтобы сказать это; это просто природа вашего рендеринга. Трудно так работать.
Большинство ваших зависимостей от ресурсов (или должны быть) таким образом.
Если вы выполняете какую-либо операцию рендеринга в текстуру через кадровый буфер, то потребителю, вероятно, даже не нужно знать о зависимости. Вы можете просто установить соответствующую внешнюю зависимость для рендеринга и подпроцесса, который его генерирует. И вы, наверное, знаете, почему вы сделали опцию рендеринга в текстуру, и вы, вероятно, знаете, куда она идет. Если вы делаете RTT для отражения, вы знаете, что местом назначения будет какая-то шейдерная схема выбора текстур. И если вы не знаете, как это будет использоваться, тогда вы можете просто быть в безопасности и установить все биты этапа назначения.
То, о чем вы говорите, имеет определенный смысл, если вы имеете дело с потоковыми объектами, когда объекты с некоторой регулярностью появляются в памяти и выводятся из нее. Но даже тогда это не является собственностью каждого отдельного ресурса.
Когда вы загружаете поточный блок, вы загружаете его данные, генерируя команды буфера (ов) и отправляя их. И вот здесь у нас есть расхождение, зависящее от реализации. Лучшая ставка на производительность - выполнить эти CB в очереди, предназначенной для операций передачи. Но поскольку Vulkan не гарантирует, что они есть у всех реализаций, вам необходимо иметь возможность доставлять эти CB переноса в основную очередь рендеринга.
Таким образом, вам нужен способ связи с потоками рендеринга, когда они могут начать использовать ресурсы. Но даже это не должно быть на основе ресурсов; им можно сказать, что "материал из блока X доступен", и затем они могут начать его использовать.
Кроме того, это расхождение в реализации становится важным. Видите, если это сделано в другой очереди, барьер не является правильным примитивом синхронизации. Ваши CB рендеринга теперь должны ждать отправки своих пакетов на семафор. И этот семафор должен обрабатывать все потребности памяти в синхронизации (то есть: целевые биты - это все). Таким образом, в реализации, где CB переноса выполняются в той же очереди, что и ваши CB рендеринга, вы также можете избежать некоторых проблем и создать единый барьер в конце CB переноса, который делает все данные ресурсы доступными для всех этапов.,
Как уже говорилось ранее, этот вид автоматизированной системы полезен только в том случае, если вы не имеете реального контроля над структурой рендеринга. Это было бы принципиально верно, если вы пишете какое-то промежуточное программное обеспечение, где высокоуровневый код определяет структуру рендеринга. Однако, если это так, Vulkan, вероятно, не подходит для этой работы.