Вспоминали тут тему багов драйверов, решил еще посмотреть, какие костыли есть в Carbon для этого, где только Dx12 и Metal
1. на старых макосях (до 10.15) определяет, что гпу от нвидии, и выключает инстансинг мешей и отрисовку множества турелей. g_brokenMacOSNvidiaDrivers
2. на макоси в целом на метале, при обнаружении гпу от нвидии или интела, выключает параллельную запись команд на рендер. g_useParallelEncoding
3. отдельно на макоси на метале, при использовании интеловой видюхи, обрезает буферы команд по 32 команды максимум
4. на макоси подменяет формат буфера глубины с D24S8 на D32, потому что на интеле и амд оно эмулируется, а на армовых устройствах недоступно вообще
5. багованый обход бага старых нвидий). На 7 серии GeForce пытаются изменить сортировку цветов при DXT сжатии, но опечатались в ifdef, поэтому по факту фикс не работает
6. отдельный обход бага софтового виндового рендера warp для сжатого формата BC7, который падает при попытке создать второй вью на ту же BC7 текстуру
7. отдельный обход "бага драйверов" без уточнений, чтобы области атласа текстур освобождались не сразу, а по номеру кадра, чтобы не поймать гонку
8. отдельный обход бага nvidia aftermath, который при включении вместе с дебаг слоем дх12 приводит к утечке ссылки на девайс и блокирует восстановление после device lost
9. отдельный шейдер для dlss шарпенинга вместо официального от нвидии из-за багов в нём
10. отдельный детект nvidia optimus, amd dynamic switchable и тд, тоже с багом (последний флаг перетирается на false всегда), и это потом выставляется в питон, чтобы игровая логика при управлении рендером могла что-то обойти
Модель работы очень похожа на VkPipelineCache, но чуть адекватнее - приложение может само задать имя кешу, а не просто получает его от драйвера. И если блоб кеша протух, то драйвер не молча пойдет по долгому пути полной компиляции, а вернет явный HRESULT с ошибкой кеша
Начиная с Windows 11 еще добавили ShaderCacheSession, которая по сути представляет собой полностью доступный приложению кеш произвольных байт, с автоматической инвалидацией по версии. Очень удобная штука, но, к сожалению, предназначена вообще для другого - для специализации шейдеров в рантайме и трансляции в байткод DirectX 12 на лету
Что забавно, у сущностей DXR (рейтрейсинг) явного кеша нет вообще, только неявные на уровне драйвера, в отличие от других API
4. Metal
Тут интересно, потому что есть старый механизм кеширования, появившийся в Metal 2 и поддерживаемый до сих пор, а есть новый из Metal 4
Старый - это MTLBinaryArchive, почти то же самое, что VkPipelineCache и ID3D12PipelineLibrary. Непрозрачный блоб драйвера, который приложение может сохранить на диск и надеяться, что он будет актуален и ускорит сборку пайплайна. В новом Metal 4 его аналог назвали MTL4Archive, но они оба совместимы со всеми версиями метала. Передавать надо было в каждый дескриптор, чтобы сохранить его в этот блоб кеша, как в вулкане и дх12
Но в Metal 4 добавили и полностью новый механизм, сериализатор компилятора шейдеров. Можно MTL4Compiler передать сериализатор, который он будет использовать при сборке всех шейдеров, проходящих через него
Это заставит его создать читаемый json файл шейдер дескрипторов рядом с их байткодом. А этот файл потом можно скормить в metal-tt из SDK, чтобы он собрал байткод шейдеров уже в нативный бинарный формат нужного таргета (внутри MTL4Archive, но оффлайн)
То есть, эта новая фича уникальна, потому что единственная из всех API позволяет еще на этапе сборки приложения собрать шейдеры в полностью нативный бинарный формат нужной GPU, полностью минуя этап компиляции байткода, который и вызывает все эти "компиляции шейдеров" на старте современных игр
Отсюда же получается, что MTL4Archive - универсальный формат кеша, который может наполняться как драйвером в рантайме, так и оффлайн компилятором шейдеров заранее во время билда, а читается потом одинаково
5. wgpu
В веб спецификации WebGPU вообще ничего похожего на кеши нет, но wgpu на нативных платформах решили сделать поддержку
Правда пока там кое-как поддержаны только VkPipelineCache, то есть работы еще много. Но HAL уже реализован, при желании можно довольно просто сделать поддержку ID3D12PipelineLibrary и MTLBinaryArchive как минимум
Решил погрузиться в вопрос кеширования графических пайплайнов
Тема не новая, поскольку это самая долгая и затратная часть инициализации любого приложения, работающего с графическими API. И предкомпилировать нормально нельзя, поскольку приложения носят с собой максимум байткод шейдеров, а реальная компиляция происходит в видеодрайвере при создании пайплайна
Есть неявные кеши в драйверах, доступа к которым нет, так что на них можно максимум надеяться. А есть явные инструменты в современных графических API, чтобы управлять кешами самим из кода
Собственно, что нашлось по последнему:
1. OpenGL
Ничего нет, он был заброшен до того, как это стали оптимизировать. Максимум в последних на момент его смерти версиях, доступных только на Linux и Windows, позволили таскать с собой байткод вулкана вместо текстовых шейдеров, но на этом всё
2. Vulkan
Вот тут дорого-богато, в кои-то веки
В самом ядре вулкана с древних времен есть VkPipelineCache, что по сути представляет собой непрозрачный блоб, предоставляемый драйвером приложению. Приложение не может узнать, что там лежит, не может проверить или почистить его, только снести целиком и пересоздать, либо смержить несколько подобных файлов
Этот кеш один на всё приложение, поэтому оно должно само его версионировать, вовремя очищать старые версии, перегенерировать по нужде и тд. И надеяться, что при передаче драйверу оно ускорит создание пайплайнов
Работает примерно так - создаете кеш, потом передаете его в вызовы на создание пайплайнов, драйвер при их исполнении будет его наполнять (раздувать тот самый непрозрачный блоб), потом в какой-то момент получаем содержимое блоба и сохраняем на диск, потом используем при последующих созданиях пайплайнов, и может быть что-то ускорится
В вулкане 1.3 в ядро добавили расширение VK_EXT_pipeline_creation_cache_control (90% доступность), которое позволяет выставить флаги при создании пайплайна, чтобы прервать его создание при невалидном кеше (чтобы не тратить кучу времени на это без него). В той же версии добавили в ядро VK_EXT_pipeline_creation_feedback (95% покрытия), который позволяет получить статистику после создания пайплайна - был ли использован кеш, сколько заняла компиляция и тд
В вулкане 1.4 же в ядро добавили новое расширение VK_KHR_pipeline_binary. Пока покрытие у него не ахти (меньше 50% устройств), но в перспективе оно сильно улучшит ситуацию
основное отличие от прошлого механизма - здесь четкое соответствие 1 пайплайн - 1 бинарник кеша, содержащий непрозрачный ключ и непрозрачный блоб
так как теперь это не единый блоб на всё, а отдельные мелкие бинарники, их можно самостоятельно менеджерить как угодно, вычищать куски устаревших пайплайнов и в целом жить заметно лучше без полного удаления и пересоздания с нуля чуть что
Это же, к слову, позволяет реализовывать удобнее фичи вроде Steam кеша шейдеров, когда на CI машинах с целевыми GPU захватываются такие бинарники и поставляются с игрой. Но это требует огромной инфраструктуры, с разными GPU, разными версиями драйверов и тд, что подавляющему большинству недоступно
Также в 1.4 добавили VK_EXT_graphics_pipeline_library (от 15% до 90% покрытия на разных платформах), который позволяет разрезать вертексные и фрагментные шейдеры на 4 куска и компилировать их отдельно, чтобы сократить количество отдельных компиляций для слабо различающихся шейдеров (те же пермутации материалов, которыми славится UE 5). Похожие шейдеры собираются в библиотеки, которые линкуются друг с другом, вместо пересборки с нуля
И еще вне ядра вообще есть VK_EXT_shader_module_identifier (от 25% до 80% покрытие платформ), который позволяет попробовать создать пайплайн по хешу кеша (если вдруг он закеширован в драйвере), не пытаясь собрать его с нуля