CG & C++ blog
57 subscribers
13 photos
2 files
129 links
Краткий обзор публикаций, презентаций, докладов по графике и C++
Download Telegram
Обнаружил такую особенность видеокарт AMD:


sh
a
derUniformBufferArrayNonUniformIndexingNative:   false,
shaderSampledImageArrayNonUniformIndexingNative: false,
shaderStorageBufferArrayNonUniformIndexingNative: false,
shaderStorageImageArrayNonUniformIndexingNative: false,
shaderInputAttachmentArrayNonUniformIndexingNative: false,


Из до
кументации Vulkan API:

shaderUniformBufferArrayNonUniformIndexingNative is a boolean value indicating whether uniform buffer descriptors natively support nonuniform indexing. If this is VK_FALSE, then a single dynamic instance of an instruction that nonuniformly indexes an array of uniform buffers may execute multiple times in order to access all the descriptors.


То есть в пределах одного варпа доступ к массиву ресурсов (буферы, текстуры и тд) может быть неоптимальным.
Эта фича часто используется в трассировке лучей и полностью поддерживается на NV RTX.
Мобильные GPU на Android и iOS в основном имеют нативную поддержку, получается только AMD отстает.
#amd_gpu #vk
Книга 3D Graphics Rendering Cookbook
Материал по OpenGL и Vulkan для продвинутых начинающих.
Зато для сцены используется Data oriented design, что не часто встречается, стоит ознакомиться.
#gl #vk #ecs #dod
Особенность выравнивания типов в шейдерных языках.

float3

GLSL: размер 12, выравнивание 16.
Metal: размер 16, выравнивание 16.
Metal packed_float3: размер 12, выравнивание 4.

float2x2

GLSL std140: размер 32, выравнивание 16, совпадает с float4x2.
GLSL std430: размер 16, выравнивание 8.
Metal: размер 16, выравнивание 8.

float3x3

GLSL std140: размер 48, выравнивание 16, совпадает с float4x3.
GLSL std430: размер 48, выравнивание 16, совпадает с float4x3.
Metal: размер 48, выравнивание 16, совпадает с float4x3.
HLSL (Vk): размер 48, выравнивание 16, совпадает с float4x3.
HLSL (DX): размер 32, выравнивание 4.

Массивы

GLSL std140: минимальное выравнивание 16
GLSL std430: выравнивание совпадает с типом данных
Metal: выравнивание совпадает с типом данных

В некоторых случаях даже использование HLSL для всех GAPI не помогает и размер данных различается.
Чтобы справиться с плавающим смещением пришлось написать свой генератор структур, где на вход дается структура на одном из языков, а на выход генерируется C++, GLSL, MSL с одинаковыми смещениями.
#vk #metal #blog
Advanced Shading Techniques with Pixel Local Storage
Примеры использования внутренней памяти тайла (tile/pixel local storage).
Поддерживается на OpenGLES с Mali GPU, а так же на Metal с Apple GPU.
На Adreno используется другое расширение, только для сложного блендинга.
Поддержка TLS/PLS на Vulkan добавленна только в новых версиях API и только в новых девайсах из-за невозможности обновления графических драйверов.
#cg #gl #vk #OIT #mali
Зачем нужен безразмерный массив юниформ в шейдере
В шейдере можно не указывать размер массива ресурсов (юниформ), но в Vulkan обязательно указывать количество дескрипторово в VkPipelineLayout.
Получается, что безразмерный массив в шейдере никак не избавляет от создания нескольких PSO под разные размеры массива.
В чем же преимущество:
1. Не нужно компилировать несколько шейдеров и хранить несколько бинарников.
2. На мобильных девайсах количество ресурсов сильно ограниченно, поэтому нельзя захардкодить массив на 1000 элементов.

Также, по документации Vulkan, нельзя оставлять дескрипторы без ресурсов, это может привести к крэшу, так как в некоторых случаях происходит неявный доступ к ресурсам, который пользователь никак не контролирует.
Фича nullDescriptor позволяет обойти эту проблему и использовать один большой массив, но может повлиять на производительность.

Новое расширение buffer_device_address дает большую гибкость - можно сделать storage buffer с динамическим массивом uint64_t/uvec2, что не требует разных VkPipelineLayout, но пока это расширение поддерживается только на десктопах и топовых мобилках.
#vk #blog
Как у меня сделана проверка на корректность синхронизаций.

Для этого я написал логгер команд, который выдает читаемый лог вызовов Vulkan комманд и результат не меняется в зависимости от запусков, что позволяет следить за изменениями. Но все синхронизации придется один раз вручную проверить на корректность.
Пример лога
Исходники тут

Другой вариант - запустить vkconfig и включить полную валидацию синхронизаций - Synchronization preset.
Guide to Vulkan Synchronization Validation

Либо из кода через расширение VK_EXT_validation_features включить VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT.
Так как VK_EXT_validation_features - расширение слоя валидации, то оно не указано в vkEnumerateInstanceExtensionProperties.

Пару лет назад я тестировал открытые движки и фреймворки на правильность синхронизаций и слой валидации легко находил ошибки. Из чего я сделал вывод, что никто не трогал настройки слоев валидации. Только мой FG автоматически расставлял синхронизации без ошибок.
#blog #vk
Часто вижу в примерах по Vulkan код вида:

GetMemoryTypeIndex( mem_req.memoryTypeBits, DEVICE_LOCAL );
Это пошло с первых примеров, тогда на дискретных ГП были такие типы памяти: DEVICE_LOCAL, HOST_COHERENT, HOST_CACHED.
Но с тех времен кое-что изменилось - добавили DEVICE_LOCAL | HOST_COHERENT память для прямого доступа со стороны ЦП, обычно эта память ограничена в 256 Мб, что достаточно для юниформ буферов. Но эта же память может использоваться для staging буферов и всех DEVICE_LOCAL буферов, если этот тип памяти окажется в списке раньше других. То есть сейчас все работает как раньше только потому, что новый тип памяти добавили в конец списка и перебор до него не доходит.

Правильнее передавать больше флагов:

uint GetMemoryTypeIndex (uint memoryTypeBits, VkMemoryPropertyFlags includeFlags, VkMemoryPropertyFlags optFlags, VkMemoryPropertyFlags excludeFlags)
Пример использования:

GetMemoryTypeIndex( mem_req.memoryTypeBits, include: HOST_VISIBLE, opt: HOST_COHERENT, exclude: DEVICE_LOCAL );
Возвращает:
1. HOST_VISIBLE | HOST_COHERENT - с optFlags.
2. HOST_VISIBLE | HOST_CACHED - без optFlags.

#blog #vk
Обзор презентаций с Vulkanised 2023

Mesh shading best practices
Повторяют все что было в печатном виде.

Ray Tracing: delivering immersive gaming experiences on mobile
Трассировка лучей на ARM. Интересно помотреть демку. Большая часть доклада - пересказ основ трассировки лучей, с 21:04 начинается best practices.

Source-level Shader Debugging in Vulkan with RenderDoc
Рассказывают как компилировать шейдеры чтоб работала отладка шейдеров в RenderDoc, поддерживается только VS, FS, CS.
Еще в 2019 мой GLSL-trace поддерживал все типы шейдеров включая меш шейдер и рейтресинг. Множество багов было отловленно им. Даже была версия под RenderDoc.

Transitioning to Vulkan for Compute
17:44 - жалуются что отладка компьют шейдеров в RenderDoc работает для одного потока, а нужно смотреть на всю сабгруппу/воркгруппу и расшаренную память, а в моем GLSL-trace это поддерживается.

VkFFT - a story of Vulkan Compute GPU HPC library development
Интересно послушать историю разработки.

Game Optimization: Radeon Developer Tools on RADV and Steam Deck
Для разработки под SteamDeck может быть полезно.

Keeping your staging buffer fixed size!
Короткий доклад про организацию стриминга данных со стороны ЦП на ГП.
Мне не нравится идея auto submission on overflow, так как сабмиты работают медленно, а для продолжения стриминга придется ждать завершения предыдущих комманд.
Я за подход, когда каждый кадр отправляются данные параллельно с рисованием.

Improving performance with adaptive Variable Rate Shading
VRS на мобилках. Не похоже, чтобы производительность сильно изменилась, да и поддерживается только на новых Mali.

Using ANGLE as a system graphics driver
Инженер из Samsung рассказывает как оптимизировали ANGLE под Андроид.
На 10:22 показано насколько ANGLE медленее нативного GLES.
11:08 - один из пунктов Fast mipmap generation using AMD's SPD library это генерация мипов за один вызов тяжелого компьют шейдера, при этом теряется DCC, расходуется больше регистров, а также старые Mali не имеют общей памяти воркгруппы, в общем не самая универсальная оптимизация.
#vk
По документации Vulkan в некоторых случаях содержимое ресурсов может быть потеряно:
* Барьер с oldLayout = UNDEFINED.
* Ресурс с exclusive sharing используется в разных очередях без queue ownership transfer (нужно всегда явно передавать ресурс из одной очереди в другую, что иногда неудобно).
* Аттачменты рендер пасса с loadOp = DONT_CARE и storeOp = DONT_CARE.
* Несколько ресурсов используют одну память, но только один ресурс содержит валидные данные.
* Неиспользуемые аттачменты в сабпасе рендерпасса, если не используется как preserve attachment.

Это нужно чтобы драйвер выбирал более быстрое поведение, например сжатый рендер таргет (delta color compression) при смене лейаута на GENERAL тратит дополнительное время на расжатие, а oldLayout = UNDEFINED позволяет это пропустить, потеряв содержимое текстуры, но, например, при такой смене лейаутов: UNDEFINED (было SHADER_READ) -> SHADER_READ, содержимое не изменится.
На тайловых архитектурах loadOp = DONT_CARE и storeOp = DONT_CARE позволяют не загружать данные из глобальной памяти в кэш тайла, но на других архитектурах данные никуда не загружаются, поэтому содержимое сохраняется.

Таким образом легко допустить ошибку и продолжить использовать содержимое текстуры, тогда как на другом драйвере или железе поведение изменится.
Для этих случаев у меня есть самописный слой валидации, который принудительно чистит содержимое ресурсов случайными значениями, чтобы поведение было одинаковым на всех системах.
#blog #vk #engine
Особенности present mode в Vulkan

VK_PRESENT_MODE_FIFO_KHR - то что раньше называли vsync. Захватывается swapchain image, рисуется, отправляется в presentation engine, затем по сигналу выводится на экран. Главный недостаток - блокировка на стороне ЦП при вызове vkAcquireNextImageKHR и vkQueuePresentKHR, из-за этого поток простаивает 8мс (при 100Гц экране), если нет свободного swapchain image. На разных устройствах блокируются в разный момент времени, на NV в vkQueuePresentKHR, на Intel и смартфонах - vkAcquireNextImageKHR. Использование VkFence ничего не меняет на NV. На некоторых смартфонах ситуация еще хуже - при вызове Acquire/Present из разных потоков происходит более долгая блокировка в 50-100мс. Преимущество - в таком режиме рисуется только то, что попадает на экран, а значит более энергоэффективно, потому что драйвер часто понижает частоты ГП при низкой нагрузке.

VK_PRESENT_MODE_MAILBOX_KHR - рендеринг идет без задержек, а на экран выводится последнее отрендеренная картинка, если частота рисования выше частоты экрана, то часть картинок теряется. Исправляет все блокировки ЦП режима FIFO, но более энергозатратный.

VK_PRESENT_MODE_IMMEDIATE_KHR - сразу же выводит картинку на экран, что может привести к наложению двух кадров (visible tearing). Этот режим не поддерживается на смартфонах. Может быть использован в бенчмарках, где важна максимальная производительность, а не картинка на экране.
#blog #vk
Memory limits with Vulkan on Mali GPUs
Пишут, что память для хранения вершин ограничена в 180Мб, превышение приводит к ошибке, даже если API используется корректно.
Это связано с тайловым рендером, когда вершины сначала трансформируются, потом выгружаются в память и используются частями для каждого тайла. Чем больше вершин, тем больше нагрузка на шину памяти и больше тепловыделение.
Размер памяти никак не зависит от формата вершин, так как хранится только позиция и дополнительная информация, это умещается в 64 байта на вершину.
Лимит относится к смартфонам 2019 года и скорее всего будет постепенно повышаться.

По этой причине техники типа Nanite не будут так эффективно работать на тайловой архитектуре (TBDR), это относится и к теселляции и к меш шейдерам. Обойти ограничения можно только генерацией примитивов внутри тайла, но пока такие техники не появились, остаются старые техники типа relief mapping.
#vk #mali
VK_KHR_dynamic_rendering proposal
1. Other APIs have much more flexible APIs for the same functionality
2. Most of the render pass API in Vulkan goes unused
3. Most applications do not or cannot use subpasses, but still pay the cost of setting them up
4. The API does not fit into most existing software architectures
5. Fundamentally, other than load/store actions, they do not address real issues for IHVs or ISVs
6. When teaching Vulkan as an API, this is a huge place where people trip up

Объяснение, почему Vulkan становится больше похож на OpenGL и прочие старые API.
Рендер пассы делали для наилучшего использования тайловой архитектуры (TBDR), но из-за этого они стали слишком сложными, что излишне для большинства десктопных видеокарт. Проблему решили радикально и избавились от рендер пассов совсем.

Ранее я уже писал о потере данных между сабпассами рендер пасса: тут.
#vk
VK_EXT_shader_object proposal
Интересны первые 2 главы, объясняющие зачем понадобилось переделывать API.
Большинство разработчиков вместо того чтобы переписать всю архитектуру заново под PSO, сделали мапу и поиск по ней для каждого рендер объекта, чтоб как и раньше менять состояния. Это приводит к множественным компиляциям пайплайнов и тормозам на стороне ЦП.
При этом драйвер намного лучше может справиться с аналогичной задачей, поэтому и решено было отказаться от больших PSO в пользу раздельных шейдеров и динамических состояний.
Спустя 7 лет с выхода Vulkan многие так сделали быстрый порт на Vulkan без необходимых оптимизаций и кардинальных пересмотров архитектуры.
#vk
Optimizing Compute Shaders for L2 Locality using Thread-Group ID Swizzling
Суть в том, что память текстуры расположенна нелинейна, а в виде Z-curve, morton order и тд.
Поэтому при последовательном доступе начинаются кэшпромахи.

Еще в далеком 2018 я написал прогу, которая за счет хака получает данные текстуры с optimal_layout, в результате чего можно увидеть как идет перестановка данных.
Сейчас обновил код: detect z-curve.

Шаблон перестановки пикселей зависит от производителя и от поколения ГП, но чаще всего минимальный размер тайла 4х4 для 32-битного формата.
#vk #gpu_opt
Doing dynamic resolution scaling? Watch out for texture memory size!

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

Проверил на AMD RX570, Vulkan возвращает разное выравнивание, в одном случае это 256 байт, а в другом 128Кб, поэтому такая разница в размерах. Похоже что у AMD есть оптимизация для тайлов 256x128 для 32-битных форматов.
Воспроизводится только с usage = transfer_dst | sampled, добавление color_attachment устраняет эту проблему.

Это не единственная проблема, в редких случаях драйвер может возвращать меньшее выравнивание, чем требует спецификация, что приводит к проблемам. Например AMD возвращал выравнивание 4 байта для scratch buffer, где требуется 256 байт.
#vk
В Vulkan добавили расширения для более явного поведения сабгрупп и квадгрупп.

По стандарту вспомогательный поток (helper invocation) может не создаватся, даже если используются операции с сабгруппами.
Например на Mali Valhall код subgroupQuadBroadcast( gl_HelperInvocation, 0 ) + ... всегда возвращает 0, то есть вспомогательные потоки не вызываются, если явно или неявно не используются деривативы.
Это можно проверить вызвав subgroupQuadBroadcast( 1, 0 ) + ..., результат будет менее 4, на углах треугольника. Что даст некорректный результат при использовании квадгрупп, поэтому и появилось расширение GLSL_EXT_shader_quad, где layout (full_quads) in; не дает драйверу оптимизировать вспомогательные потоки.

Для корректной работы дериватив нужно чтобы все потоки квадгруппы выполняли одинаковый код, для этого добавили функции quadAny() и quadAll().
#vk
Обертка над Vulkan, управление ресурсами

Удаление ресурсов должно происходить, когда ГП закончил все операции над ресурсом, то есть командный буфер завершил выполнение. Для этого есть несколько подходов, в FG в командном буфере увеличивается счетчик ссылок для каждого используемого ресурса и добавляется в список, по завершению выполнения идет проход по списку и уменьшается счетчик ссылок. Это увеличивает нагрузку на ЦП, но не так существенно - трейс Doom 2016 выполнялся на FG на 60 fps в одном потоке. Похожий подход используется внутри Metal API.
Но можно сделать лучше - если обертка используется для рилтайм графики, то работает с фиксированной частотой кадров, значит при начале нового кадра 'кадр - 2' уже завершился и можно освобождать ресурсы. Теперь счетчик ссылок управляется только пользователем, когда он дойдет до нуля, ресурс добавится в список на удаление и по завершении кадра удалится. Так в AE нагрузка на ЦП стала меньше.
В DE сделано аналогично, но ресурс удаляется при завершении кадра на каждой из очередей (graphics, compute).

Кроме удаления ресурсов есть еще staging buffer, который скрывает от пользователя работу с памятью под промежуточный буфер. В FG командный буфер захватывал отдельный буфер, например на 16МБ, когда память заканчивалась захватывался дополнительный буфер. При долгом выполнении командного буфера, промежуточные буферы продолжают висеть как используемые.
В AE благодаря привязке к кадрам механизм улучшился - выделяется один статичный буфер на кадр (обычно до 4Мб) и атомарными операциями смещается текущая позиция в нем. Когда требуется загрузить больше данных, то выделяются динамические буферы, они могут переиспользоваться в течение нескольких секунд, иначе удаляются. Пользователь может задать лимит памяти на каждый кадр, это нужно для стабильного fps. При превышении лимита память не выделяется, а пользователю возвращается размер данных, которые не получилось скопировать.
Каждый кадр данные перегоняются по PCI-E шине, пропускная способность у 3й версии - 16Гб/с, на 60fps это 273Мб/с при условии, что копирование произойдет в начале кадра. Если превысить этот лимит или копировать в середине/конце кадра, то время кадра увеличится и fps станет нестабильным. Этот механизм позволяет стримить сцены без async transfer очереди без влияния на рендер, драйвер сам парллелит копирование пока нет синхронизаций.
В DE память выделяется по мере необходимости и переиспользуется, но при превышении лимита каждый кадр происходит выделение и освобождение памяти, что очень медленно - до 10fps в однопотоке. Отдельно выделяется статичный буфер под юниформы (обычно 8Мб), при превышении лимита вызывается vkDeviceWaitIdle и память безопасно переиспользуется.
#vk
Vulkanised 2025 (playlist)

Using Ycbcr Samples with Bindless Vulkan
Предлагает использовать specialization constant для Ycbcr immutable sampler, так как динамические индексы не работают.

ARM ASR: Platform Agnostic Efficient Temporal Upscaling on Mobile
Оптимизировали FSR2 под свое железо. В разы уменьшили нагрузку на шину памяти, что важно для мобилок.

Integrating Bindless Vulkan with Ray Tracing on Mobile Devices
Рассказывают про трассировку лучей с самого начала. Демка с AO и тенями.

Blender Transition Towards Vulkan
Немного про рендер граф, компиляцию шейдеров, слои и отладку. Сделали тулзы для симуляции разных ГП (так многие делают).

A New Open Source Profiler & Debugger for Android + Vulkan
Рассказывают про Android GPU Inspector.

Implementing Foveated Rendering with OpenXR and Vulkan
Рассказывают про VRS в обоих вариантах: fragment shading rate и fragment density map.

Inspecting Shader Value Using GPU-Driven Rendering
Сделали рисование линий из шейдера, нужно для визуализации значений переменных, также линиями можно выводить числа.

Crash Diagnostic Layer
Слой валидации который упрощает поиск места падения драйвера. Не работает на уровне шейдеров.

Debugging Your GPU Workflow
Про GPU Assisted Validation - валидация на уровне ГП, включает шейдеры, indirect параметры, индексы вершин.
#vk
Vulkanised 2025, продолжение

libGPULayers: Diagnostic Vulkan Layers for Android
В ARM разработали слой libGPULayers, чтобы помогать другим разработчикам: находить ошибки при работе с Vulkan, анализировать производительность.
Это сложнее чем исправить код в приложении или подправить шейдер.

Vulkan Best Practices for Mobile Development
С новым поколением потребление энергии (и тепловыделение) памятью уменьшается незначительно, тогда как энергоэффективность процессоров растет, поэтому для мобильных платформ важно уменьшить нагрузку на память. Рекомендации по синхронизации. Рассматриваются техники пропуска невидимых пикселей (HSV, DVS, FPP).

What is Maximal Reconvergence and Why it Matters
Рассказывает как работает SIMT, вырожденные потоки (divergence). Ветвление создает вырожденные потоки, после ветвления все потоки снова выполняют одинаковые инструкции, но в зависимости от реализации они могут остаться вырожденными, что приводит к неопределенным результатам при операциях с subgroup. Maximal reconvergence исправляет эту ситуацию.

Color Volume Transformations in Vulkan Shaders
Как появились цветовые пространства. Чем отличаются цветовые пространства, тонемапинг.

Swapchains Explained: How to Manage Them Effectively
Кроме пересказа документации еще расказывают о деталях вывода на экран Android. Разбираются проблемы синхронизации.
#vk
Vulkanised 2025, нейронки

Practical Neural Texture Compression
Тоже самое что в документации по NV NTC, только в виде презентации.

Slang is for Neural Graphics
Добавили в язык поддержку градиентного спуска: [Differentiable] аттрибут помечает функции для которых генерируется производная, далее производная используется в градиентном спуске для поиска наилучших коэфициентов.
Подробнее по авто-генерацию производных в статье: SLANG.D: Fast, Modular and Differentiable Shader Programming

Using Neural Networks in Shaders
В железе нейронные шейдеры сделаны как умножение матрицы на матрицу, то есть аналогично cooperative matrix, это может уменьшить производительность при неоднородном выполнении (nonuniform).
Ссылаются на статью Real-Time Neural Appearance Models, где нейронка с BRDF работает в 10 раз быстрее и точнее, чем аналитический BRDF с многослойным материалом.
#vk