Рендер пассы в Vulkan в основном нужны для оптимизации тайлового рендера в мобильных GPU (tile based deferred renderer), для этого в них используются сабпассы.
В начале рендер пасса данные аттачментов загружаются в кэш тайлового рендера, если loadOp = Load, либо очищаются (или помечаются как очищенные) если loadOp = Clear, либо содержимое кэша неопределено если loadOp = DontCare. В целях безопасности скорее всего DontCare работает аналогично Clear, чтобы никто не мог прочитать предыдущие данные из кэша.
При завершении всех сабпассов данные из кэша выгружаются в глобальную память если storeOp = Store, либо не выгружаются, если storeOp = DontCare. Каждый сабпасс определяет какие из аттачментов используются в данный момент. Если аттачмент никак не используется в сабпассе, то его данные к кэше могут быть затерты и переиспользованы, чтобы этого не произошло в описание сабпасса есть preserve attachment.
От количества используемых аттачментов в сабпассе определяется разрешение тайла, так как размер памяти ограничен. Размер тайла фиксирован для всего рендер пасса и определяется как минимальный среди всех сабпассов.
По спецификации Vulkan каждый сабпасс может выполняться в любом парядке и параллельно с другими. На нынешних GPU такого не происходит и все сабпассы и рендер пассы выполняются последовательно, но чтобы и в будущем не возникло проблем нужно расставлять зависимости между сабпассами.
При этом даже потребительские карты от NVidia содержат около 6 независимых блоков, которые способны рисовать параллельно, но эта функция заблокирована в драйверах и доступна только на проффессиональных GPU, хотя были новости, что под Linux написали драйвера с поддержкой параллельного рисования. Но даже там где драйвер позволяет параллельное рисование, эта функция применяется для разных приложений, например при использовании нескольких виртуальных машин на одной машине. На мобильных GPU и так рисование идет потайлово, поэтому от распараллеливания рисования смысла нет, к тому же кэш тайла будет поделен между несколькими сабпассами, что тоже плохо.
Неявные зависимости
Если initialLayout отличается от layout, используемого в сабпассе, то создается неявная зависимость с (srcSubpass = External, dstSubpass = N), аналогично и для finalLayout - создается (srcSubpass = N, dstSubpass = External). Неявные зависимости внутри рендер пасса не создаются, поэтому их обязательно задавать вручную.
Явные зависимости
Как и для барьеров есть два типа зависимостей:
srcStageMask = EarlyFragmentTests | ColorAttachmentOutput
dstStageMask = EarlyFragmentTests | ColorAttachmentOutput
- Раyнее отсечение всего треугольника.
- Растеризация треугольника.
- EarlyFragmentTests где происходит тест глубины и трафарета (stencil) и запись в них.
- Вызывается фрагментный шейдер.
Для preserve attachment достаточно только execution dependency, но для цепочки (color -> preseve -> input) уже потребуется memory dependency в одном из промежутков.
В описании заивисмости есть набор флагов dependencyFlags, где ByRegion - зависимость между тайлами, без нее нет преимущества от сабпассов на мобильных девайсах, ViewLocal - зависимость между отдельными view, это нужно для multiview rendering, например для VR.
В Vulkan поддерживается одновременное чтение из input attachment и запись в color attachment, для этого у аттачмента должен быть одинаковый imageLayout = General, так как только он поддерживает оба типа операций. Далее требуется subpass self dependency - когда srcSubpass == dstSubpass, это позволяет вызывать vkCmdPipelineBarrier() внутри рендер пасса. Теперь можно рисовать геометрию без перекрытия и обязательно ставить барьер между каждым рисованием. Между барьерами фрагментный шейдер для каждого пикселя должен выполниться только один раз, иначе будет неопределенное поведение. В Metal и новом расширении Vulkan есть поддержка rasterization order group что упрощает написание подобного алгоритма.
Команда vkCmdClearAttachments() аналогична рисованию квадрата на весь экран или тайл и не требует как либо синхронизаций.
Совместимость рендер пассов
Если совпадает описание сабпассов и формата аттачмента, то рендер пассы совместимы. Операции loadOp, storeOp, лейауты initialLayout, finalLayout и все VkAttachmentReference::layout могут отличаться.
Это позволяет создать один фреймбуфер только для одного рендер пасса и использовать его для всех совместимых с ним. Также это ускоряет компиляцию пайплайнов, так как не требуется компилировать под все рендер пассы, только под один совместимый.
Комментариев нет:
Отправить комментарий