CG & C++ blog
57 subscribers
13 photos
2 files
129 links
Краткий обзор публикаций, презентаций, докладов по графике и C++
Download Telegram
Обзор презентаций с 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
C++20’s likely Attribute - Optimizations, Pessimizations, and unlikely Consequences (video)
Нюансы использования аттрибутов likely, unlikely.
Если кратко - все сильно зависит от кода и версии компилятора.
36:04 - likely оказался на 10% быстрее unlikely.
#cpp #cpu_opt
How to Improve Shader Performance by Resolving LDC Divergence (video)
Пример как пользоваться NSigth для профилирования кадра и шейдеров.
Показывают как замена constant (uniform) буфера на structured (storage) дает ускорение в 1.6 раз.
#nv #gpu_opt
По документации 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
Realistic smoke lighting with 6-way lighting in VFX Graph
6D лайтмапы в Юнити. Хороший способ оптимизировать объемные эффекты, чтобы избежать медленного маршинга по объему.
#volumetric
Improving job system performance scaling in 2022.2
part 1: Background and API
part 2: Overhead
Рассказывают как работает планировщик задач в Юнити, какие у него узкие места и как они оптимизировали его в новой версии.
А проблемы типичные:
* проверка, завершены ли зависимые таски.
* потери на переключение потоков (context switch).
* задержка при пробуждении потока.
* синхронизации при большом количестве потоков, в том числе и при lock-free алгоритмах.
#threading
smartphone.jpg
114.3 KB
Сравнение формата RGBA16F + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR на разных устройствах:
* VA монитор на 1000 нит
* Смартфон с AMOLED экраном на 800 нит

Особенности:
* Переход в белый цвет зависит от яркости, зеленый самый яркий и раньше становится белым.
* На мониторе диапазон 0..100 примерно совпадает с 0..24 на смартфоне.
* На мониторе используется аналог ACES тонемапинга, поэтому синий переходит в розовый, а потом в белый.
* На смартфоне используется более простой тонемапинг, который некорректно работает с чисто синим цветом.

Нет способа программно узнать характеристики встроенного тонемапинга, поэтому лучше использовать свой, а значениями > 1.0 регулировать яркость экрана.
#HDR
Как получить информацию о ЦП на Android.

Через /proc/cpuinfo можно узнать количество ядер и их описание.
processor - номер ядра.
CPU implementer - производитель ЦП (`ARM_CPU_IMP_*`).
CPU part - номер модели ЦП (`*_CPU_PART_*`).
Константы можно посмотреть здесь: arm cputype, arm64 cputype, julia/processor_arm.cpp - расширенный список, включает ЦП для IoT.

Features - список расширенных инструкций ЦП, расшифровка есть здесь: Arm CPU features table
То же самое можно получить через getauxval() с AT_HWCAP и AT_HWCAP2.

BogoMIPS - мера производительности, но часто выдает одинаковые значения для всех типов ядер.

Через /sys/devices/system/cpu/cpu[x] доступна дополнительная информация:
cpufreq/cpuinfo_min_freq, cpufreq/cpuinfo_max_freq - минимальная и максимальная частота ядра.
cpufreq/scaling_cur_freq - текущая частота ядра, используется как замена cpuinfo_cur_freq, который недоступен.

По максимальной частоте можно сгруппировать ядра по типу производительности:
* высокопроизводительные (X1, X2)
* производительные (A78)
* энергоэффективные (A55)
Либо через параметр /sys/devices/system/cpu/cpu[x]/cpu_capacity - чем он больше, тем производительнее ядро.
#blog #arm #cpu_prof
Как получить информацию о ЦП на Windows.

IsProcessorFeaturePresent - для проверки поддерживаемых инструкций типа SSE, то же самое возвращает и __cpuid().
GetSystemCpuSetInformation - возвращает подробную информацию о ЦП, например EfficiencyClass позволит обнаружить энергоэффективные ядра на новых Intel.
CallNtPowerInformation - с параметром ProcessorInformation позволяет получить частоту ядра ЦП, но параметр CurrentMhz не меняется.
GetLogicalProcessorInformationEx - с параметром RelationCache позволяет получить информацию о кэше, но часто информация неполная.

NtQuerySystemInformation - с параметром SystemProcessorPerformanceInformation позволяет получить значения счетчиков для каждого ядра, в WinAPI структуры определены без полей, но есть информация на сторонних ресурсах SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
Счетчики обновляются не так часто, при 100Гц дельта может быть 0, лучше обновлять каждую секунду. Так можно рассчитать % использования ЦП:

LONGLONG idle_time = next.IdleTime - prev.IdleTime;
LONGLONG kernel_time = next.KernelTime - prev.KernelTime;
LONGLONG user_time = next.UserTime - prev.UserTime;
LONGLONG total_time = idle_time + kernel_time + user_time;
float user_usage = float(user_time) / float(total_time);
float kernel_usage = float(kernel_time) / float(total_time);

С параметром SystemPerformanceInformation возвращается структура SYSTEM_PERFORMANCE_INFORMATION, где есть дополнительные счетчики производительности.
#blog #cpu_prof
Общее впечатление от GDC.
Все больше презентаций не содержат достаточно технических деталей для воспроизведения техник.
Если это многопоточка, то нет деталей реализации и оптимизации, как например в статье Unity. Возможно у всех также сделано не идеально и есть большие потери на управление тасками, поэтому и умалчивают, чтоб не облажаться.
Построение новой многопоточной архитектуры движка и игры это очень сложно, а удачных примеров все еще мало, а подробной архитектуры ААА игры вообще не найти, что замедляет развитие аналогов. Зато все переходят на UE, забрасывая свои движки, а архитектура UE тоже не идеальна.

Если это нейросети, то нет сравнения с написаным по старинке алгоритмом. Может потому что даже не способны написать подобный алгоритм?
С ИИ еще давно перестали появляться хорошие публикации, последнее что помню это от начала 2010х.
#blog
Advanced Heightmap Compression Using Deep Learning in Dune: Awakening
Используют нейросеть для сжатия/расжатия рельефа, сложная нейросеть для сжатия и более простая для расжатия, учитывая что на RTX серии и на новых RDNA есть быстрые инструкции для умножения больших матриц, то работать должно быстро. Только на остальных платформах таких инструкций нет и производительность будет хуже.
Еще странность - они сравнивают свое сжатие с jpeg, а не с BC4, не использовали Morton order и тд.
Building Night City: the technology of Cyberpunk 2077
5 - Не используют STL, исключения, RTTI. Судя по примерам кода, они делают свой RTTI, прям как в старые добрые времена. Отказавшись от STL они могут отказаться и от исключений, иначе никак.
8 - Управление памятью. Используют разнообразные аллокаторы, специализированные под свои задачи. Про потокобезопасность не сказано, есть ли lock-free аллокатор?
16 - Ограничения по памяти: 1.5Гб ОЗУ и 3Гб видео. Далее для каждой системы определены максимальные размеры памяти, чтобы не выходить за общие лимиты.
19 - Система тасков. Интересно, но очень мало на такую большую тему. В конце напишут, что думать о масштубируемости сложно, многопоточка - сложно, но почему это сложно понятно только тем, кто с этим уже сталкивался.
27 - IO. Все работает асинхронно, как и положено при использовании тасков. Используемая память ограничена 128Мб.
34 - Графика. Запускается на потоках с меньшим приоритетом, чем 'game jobs' (физика и ИИ?). Еще меньше информации, но есть много примеров, начиная со слайда 80.
Построение графа в коде неудобно читать, тут визуальное программирование пригодилось бы.
У них рендер граф может содержать таски только для ЦП, например для сортировки элементов.
74 - Не смогли полностью отказаться от главного потока, он используется только для начала и завершения кадра. Забыли указать, что часть вызовов ОС возможны только из главного потока.
Инструменты профилирования для Android.

Есть стандартные инструменты: Google AGI, Adreno GPU profiler, Mali profiler, но поддержка устройств ограничена и у меня ни один профайлер не стал работать с Vulkan.

Остается прямой доступ к счетчикам, для этого есть несколько библиотек:
HWCPipe - счетчики для ARM CPU и Mali GPU. Не поддерживается на Qualcomm CPU (Kryo) и некоторых ARM CPU, даже если в названии Cortex Axx. Пример использования можно посмотреть в Vulkan samples.

hardware-perfcounter - счетчики для Mali и Adreno, доступно намного больше счетчиков, в том числе специфичные для Adreno 5xx и 6xx.

Прямой доступ к счетчикам позволяет написать алгоритм для самооптимизации, когда идет переключение между разными алгоритмами, подгонка итераций/констант и тд.
#blog #gpu_prof #cpu_prof
Пришло время и мне рассказать про нейросети.

Начну со своего опыта, в отличие от нейросетей, где подбирали коэффициенты матриц, я пытался написать кодогенератор, то есть программу, которая по тестовым данным будет создавать наиболее оптимизированный код. Начал я с полного перебора и он работал очень медленно, но попытка использовать генетический алгоритм провалилась - он работал еще медленее, градиентный спуск вообще нельзя было применить так как менялись инструкции, а не константы и соседние результаты сильно отличались. В итоге все оптимизации были на ускорение тупого перебора, устранение дублирования кода, валидация кода до тестов, ранний выход, если тесты сильно отличались и тд.
Какой из этого можно сделать вывод - только полный перебор гарантированно дает наилучший результат, но он перестает справляться на больших данных из-за экспоненциальной сложности.

Теперь про нынешние нейросети, я их не разрабатывал и подробно не изучал, но кое-что знаю. Как происходит обучение - нейросеть гоняют на тестовых данных, она постоянно пытается "заучить" правильные ответы и из-за этого ее размер растет, но проверяющий алгоритм ищет наиболее компактное решение, поэтому выигрывает вариант который меньше заучивает, а значит он "понимает" зависимость между входными данными и результатом, то есть выводит алгоритм.
Только проблема в том, что в огромных нейросетях невозможно полностью избавиться от заученных частей, следовательно в какой-то момент нейросеть вместо применения алгоритма вернет заученый ответ и это чаще всего будет ошибкой. Другая проблема это недостаточное количество данных из-за которого выведенный алгоритм будет неточным.Чтобы проверить такую нейросеть на корректность всех ответов, нужно перебрать все возможные входные данные и проверить ее ответ. Для распознавания ситуации на дороге, для поддерживания разговора (чат) это почти бесконечный набор входных данных, то есть ошибки будут и будут всегда, какой бы совершенной не была бы нейросеть.
Остается вариант комбинации нейросети и написанных человеком алгоритмов, которые будут проверять результат и при обнаружении ошибок добавлять новые тестовые данные. А чем больше тестовых данных тем дольше будет обучение и тем дороже оно обойдется.
#blog
Что следует помнить C++ разработчику об архитектуре процессора
Часто в таких докладах повторяются одни и те же рекомендации, тут также, но кое-что разобрано более детально, это интересно послушать:
14:37 - про конвеер ЦП.
19:46 - параллельное выполнение инструкций, разбор разных архитектур ЦП.
22:48 - про кэш. Десктопные ЦП делают префетч и для доступа по шаблону (фиксированное смещение), мобильные ЦП делают префетч только для линейных даннных.
40:05 - эмулятор ЦП, применяется для микрооптимизаций.
#cpu_opt #rus
C++ Russia 2017: Антон Полухин, Как делать не надо: C++ велосипедостроение для профессионалов
Иногда полезно пересматривать прошлые конференции, хотя кажется, что все очевидно и давно известно.
Но нашел кое-что новое для себя:
58:12 - нужно указывать noexcept для move-ctor, иначе STL контейнеры могут аллоцировать дополнительную память при копировании.

В коде VS я не нышел выделение дополнительной памяти, зато есть такое:

// _Resize_reallocate()
if constexpr (is_nothrow_move_constructible_v<_Ty> || !is_copy_constructible_v<_Ty>) {
_Uninitialized_move(_Myfirst, _Mylast, _Newvec, _Al);
} else {
_Uninitialized_copy(_Myfirst, _Mylast, _Newvec, _Al);
}
То есть move конструктор в некоторых случаях не вызывается, а происходит полное копирование.
#cpp
Особенности 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