(video) CppCon 2016: Timur Doumler “Want fast C++? Know your hardware!"
Рекомендации по оптимизации, ничего нового, но презентация хорошая.
21:52 - немного про префетчер.
23:00 - ассоциативность кэша, объясняет почему происходит резкое падение производительности при чтении с определенным шагом (256, 512 байт). Ссылается на статью Gallery of Processor Cache Effects.
28:18 - выравнивание данных.
34:20 - предсказание ветвлений.
44:10 - 'false sharing', частые синхронизации кэшей между ядрами ЦП.
48:10 - зависимость между данными в цикле.
#cpu_opt
Рекомендации по оптимизации, ничего нового, но презентация хорошая.
21:52 - немного про префетчер.
23:00 - ассоциативность кэша, объясняет почему происходит резкое падение производительности при чтении с определенным шагом (256, 512 байт). Ссылается на статью Gallery of Processor Cache Effects.
28:18 - выравнивание данных.
34:20 - предсказание ветвлений.
44:10 - 'false sharing', частые синхронизации кэшей между ядрами ЦП.
48:10 - зависимость между данными в цикле.
#cpu_opt
Ghostwire Tokyo: DLSS vs. TSR vs. FSR
Сравнение разных техник сглаживания и апскейлинга.
На скриншотах много тонких линий, что создает проблемы для реконструкции.
Но нехватает тестов в динамике на мелкие движущиеся объекты, DLSS с этим плохо справляется и оставляет шлейфы.
#AA
Сравнение разных техник сглаживания и апскейлинга.
На скриншотах много тонких линий, что создает проблемы для реконструкции.
Но нехватает тестов в динамике на мелкие движущиеся объекты, DLSS с этим плохо справляется и оставляет шлейфы.
#AA
Как у меня сделана проверка на корректность синхронизаций.
Для этого я написал логгер команд, который выдает читаемый лог вызовов 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 комманд и результат не меняется в зависимости от запусков, что позволяет следить за изменениями. Но все синхронизации придется один раз вручную проверить на корректность.
Пример лога
Исходники тут
Другой вариант - запустить 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
Еще в прошлом году планировал написать большую статью про архитектуру асинхронного движка и игры на нем, но сама архитектура часто корректировалась, так как у меня просто не было опыта в подобном.
Сейчас в планах выкладывать части статьи в виде коротких заметок, из чего потом соберу большую статью.
#blog
Сейчас в планах выкладывать части статьи в виде коротких заметок, из чего потом соберу большую статью.
#blog
Профилирование.
Тут все сильно отличается, с одной стороны разбивая код на таски я получил возможность легко написать свой профайлер, достаточно вставить замеры до и после вызова таска. Также каждый таск подписан, что упрощает привязку данных профайлера к коду, не нужно вставлять макросы в функции или использовать VTune API.
С другой стороны без визуализации сложно понять что происходит внутри кадра и в каком порядке выполняются таски. При этом обычный профайлер начинает показывать время работы планировщика тасков, что замусоривает результаты, особенно на демках, где нет большой нагрузки на потоки.
#blog #engine
Тут все сильно отличается, с одной стороны разбивая код на таски я получил возможность легко написать свой профайлер, достаточно вставить замеры до и после вызова таска. Также каждый таск подписан, что упрощает привязку данных профайлера к коду, не нужно вставлять макросы в функции или использовать VTune API.
С другой стороны без визуализации сложно понять что происходит внутри кадра и в каком порядке выполняются таски. При этом обычный профайлер начинает показывать время работы планировщика тасков, что замусоривает результаты, особенно на демках, где нет большой нагрузки на потоки.
#blog #engine
Запись команд на Vulkan и Metal.
В Vulkan все просто - есть буфер команд, он заполняется и отправляется на ГП.
В Metal немного сложнее - запись команд идет через энкодеры, переключение между которыми не бесплатное.
Каждый энкодер записывает команды, которые могут выполняться параллельно с другими энкодерами, это решает внутренний фреймграф.
У меня в движке за основу взяты энкодеры из Metal, получились контексты: DrawCtx, GraphicsCtx, ComputeCtx, TransferCtx, ASBuildCtx, RayTracingCtx.
Для Vulkan это также полезно, так как на мобильных архитектурах переключения `graphics -> compute -> graphic`s работают неоптимально и их надо избегать, также разные контексты содержат независимые друг от друга этапы, а значит они могут выполняться параллельно.
Контексты можно использовать только внутри таска
1. Создается массив командных буферов -
2. Создается
3. Внутри таска создается контекст, он принимает ссылку на таск.
4. Записываются команды.
5. В конце, командный буфер передается в
6. Когда все командные буферы записаны и переданны в
Аналогично работает параллельное рисование через
Софтварный командный буфер.
В Vulkan и Metal командный буфер может использоваться только в одном потоке. Если не ограничивать количество потоков рендера, то может создаться много командных буферов, и это особенно накладно, если записывается всего несколько команд. Намного удобнее записать команды в память и в одном потоке записать их в нативный командный буфер.
Интерфейсы контекстов тут
#blog #engine
В Vulkan все просто - есть буфер команд, он заполняется и отправляется на ГП.
В Metal немного сложнее - запись команд идет через энкодеры, переключение между которыми не бесплатное.
Каждый энкодер записывает команды, которые могут выполняться параллельно с другими энкодерами, это решает внутренний фреймграф.
У меня в движке за основу взяты энкодеры из Metal, получились контексты: DrawCtx, GraphicsCtx, ComputeCtx, TransferCtx, ASBuildCtx, RayTracingCtx.
Для Vulkan это также полезно, так как на мобильных архитектурах переключения `graphics -> compute -> graphic`s работают неоптимально и их надо избегать, также разные контексты содержат независимые друг от друга этапы, а значит они могут выполняться параллельно.
DrawCtx
- только команды рисования, работает для одного сабпасса, соответствует MTLRenderCommandEncoder.GraphicsCtx
- только для манипуляций с рендер пассами, для Metal это вызовы MTLCommandBuffer::renderCommandEncoder и MTLCommandBuffer::parallelRenderCommandEncoder.TransferCtx
- команды копирования, соответствует MTLBlitCommandEncoder и MTLResourceStateCommandEncoder.ASBuildCtx
- построение и копирование ускоряющих структур, сответствует MTLAccelerationStructureCommandEncoder, для Vulkan все выполняется на этапе VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR.RayTracingCtx
- только команды трассировки, нет аналогов на Metal. Для Vulkan это этап VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR и VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR для пайплайнов и дескрипторов.Контексты можно использовать только внутри таска
RenderTask
, последовательность выглядит так:1. Создается массив командных буферов -
CommandBatch
.2. Создается
RenderTask
, для него выделяется уникальный индекс в массиве команд.3. Внутри таска создается контекст, он принимает ссылку на таск.
4. Записываются команды.
5. В конце, командный буфер передается в
CommandBatch
.6. Когда все командные буферы записаны и переданны в
CommandBatch
, они отправляются на ГП (queue submit).Аналогично работает параллельное рисование через
DrawTask
и DrawCommandBatch
.Софтварный командный буфер.
В Vulkan и Metal командный буфер может использоваться только в одном потоке. Если не ограничивать количество потоков рендера, то может создаться много командных буферов, и это особенно накладно, если записывается всего несколько команд. Намного удобнее записать команды в память и в одном потоке записать их в нативный командный буфер.
Интерфейсы контекстов тут
#blog #engine
Низкоуровневый рендер в движке.
При планировании архитектуры был выбор из двух вариантов:
1. Привязка к кадрам. Все команды отправляются для конкретного кадра, даже async compute не может выполняться несколько кадров - при двойной буферизации 2й кадр будет ждать завершения всех команд. Но это дает и преимущества - кадр более предсказуемый, что дает и более стабильное время кадра.
2. Без привязки к кадрам. Это позволяет делать долгие асинхронные вычисления или копирования, но делает поведение менее предсказуемым.
Я выбрал вариант с привязкой к кадрам, таким образом:
* Ресурсы не удаляются сразу, а с задержкой в 2 кадра.
* Используется общий staging buffer, выделенная память гарантированно валидна в пределах кадра, это упростило работу с память. Например в при чтении из видеопамяти в
* Ограничен максимальный размер staging buffer на кадр, так чтобы все данные успели передаться по шине PCI-E за время кадра. Таким образом ГП не простаивает в ожидании данных с ЦП и время кадра более стабильное.
#blog #engine
При планировании архитектуры был выбор из двух вариантов:
1. Привязка к кадрам. Все команды отправляются для конкретного кадра, даже async compute не может выполняться несколько кадров - при двойной буферизации 2й кадр будет ждать завершения всех команд. Но это дает и преимущества - кадр более предсказуемый, что дает и более стабильное время кадра.
2. Без привязки к кадрам. Это позволяет делать долгие асинхронные вычисления или копирования, но делает поведение менее предсказуемым.
Я выбрал вариант с привязкой к кадрам, таким образом:
* Ресурсы не удаляются сразу, а с задержкой в 2 кадра.
* Используется общий staging buffer, выделенная память гарантированно валидна в пределах кадра, это упростило работу с память. Например в при чтении из видеопамяти в
ITransferContext::ReadbackImage ()
.* Ограничен максимальный размер staging buffer на кадр, так чтобы все данные успели передаться по шине PCI-E за время кадра. Таким образом ГП не простаивает в ожидании данных с ЦП и время кадра более стабильное.
RenderTask
- используется для ассинхронной записи командного буфера.DrawTask
- используется для ассинхронной записи вторичного командного буфера.CommmandBatch
- хранит масив командных буферов и семафоров для синхронизации с другими батчами и с ЦП. Аналогичен одному вызову vkQueueSubmit.DrawCommandBatch
- хранит массив вторичных командных буферов, которые затем выполняются в IGraphicsContext::ExecuteSecondary()
.RenderTaskScheduler
- переключает кадры и управляет батчами команд: создает их и отправляет на ГП, когда они заполнятся.RenderGraph
- сделан поверх предыдущих классов, добавляет отслеживание состояния ресурсов и синхронизациями между батчами.#blog #engine
Синхронизации без рендерграфа.
Когда рендер кадра состоит из заранее известных проходов, то управлять состояниями ресурсов можно и вручную.
Первым проходом должно быть обновление данных на ГП.
Синхронизация
Все копирования и обновление юниформ должно быть в этом проходе, чтоб избавиться от лишних синхронизаций в других проходах, зачем это нужно я объяснял тут (п.3).
Далее идут проходы рисования и вычислений.
Предполагается, что все неизменяемые ресурсы уже находятся в том состоянии, в котором они используются в дескрипторах.
Остаются синхронизации для изменяемых ресурсов (Attachment, StorageImage, StorageBuffer), их легко отслеживать и синхронизировать вручную, для проверки корректности есть способы.
Синхронизации между очередями.
Есть 2 подхода:
1. Сделать ресурсы общими для всех очередей (VK_SHARING_MODE_CONCURRENT), тогда достаточно сделать синхронизации семафорами, чтобы избежать одновременной записи или чтения и записи (data race), в движке это делается через
пример: Async compute + shared resources
2. Явно передавать ресурсы между очередями (queue ownership transfer). Внутри рендер таска это сложнее отслеживать, поэтому такие барьеры удобнее вынести в интерфейс
пример: Async compute + queue ownership transfer
#blog #engine
Когда рендер кадра состоит из заранее известных проходов, то управлять состояниями ресурсов можно и вручную.
Первым проходом должно быть обновление данных на ГП.
Синхронизация
Host_Write -> CopySrc|IndexBuffer|VertexBuffer|UniformBuffer
нужна при записи данных на стороне ЦП.Все копирования и обновление юниформ должно быть в этом проходе, чтоб избавиться от лишних синхронизаций в других проходах, зачем это нужно я объяснял тут (п.3).
Далее идут проходы рисования и вычислений.
Предполагается, что все неизменяемые ресурсы уже находятся в том состоянии, в котором они используются в дескрипторах.
Остаются синхронизации для изменяемых ресурсов (Attachment, StorageImage, StorageBuffer), их легко отслеживать и синхронизировать вручную, для проверки корректности есть способы.
Синхронизации между очередями.
Есть 2 подхода:
1. Сделать ресурсы общими для всех очередей (VK_SHARING_MODE_CONCURRENT), тогда достаточно сделать синхронизации семафорами, чтобы избежать одновременной записи или чтения и записи (data race), в движке это делается через
CommandBatch::AddInputDependency (CommandBatch &)
. Минус этого подхода - на AMD на общих ресурсах не включается компресия рендер таргетов, что снижает производительность.пример: Async compute + shared resources
2. Явно передавать ресурсы между очередями (queue ownership transfer). Внутри рендер таска это сложнее отслеживать, поэтому такие барьеры удобнее вынести в интерфейс
CommandBatch
, так появился метод CommandBatch::DeferredBarriers()
и initial, final
параметры при создании рендер таска. Теперь управление перемещением ресурсов происходит на этапе планирования батчей команд.пример: Async compute + queue ownership transfer
#blog #engine
(video) Optimizing Binary Search - Sergey Slotin - CppCon 2022
3:50 - вариант с ветвлением, как в stl. Так было сделано для совместимости со всеми итераторами.
8:30 - детально разбирается почему ветвления это плохо.
12:29 - вариант без ветвлений, работает в 2 раза быстрее на небольших массивах.
Для большинства этого должно хватить, а дальше начинается магия оптимизаций.
#cpp #cpu_opt
3:50 - вариант с ветвлением, как в stl. Так было сделано для совместимости со всеми итераторами.
8:30 - детально разбирается почему ветвления это плохо.
12:29 - вариант без ветвлений, работает в 2 раза быстрее на небольших массивах.
Для большинства этого должно хватить, а дальше начинается магия оптимизаций.
#cpp #cpu_opt
RDNA3 Instruction Set Architecture
* (1.2.2.1) Локальный кэш (LDS) содержит 64 атомика для быстрого доступа в преелах work-group.
* (1.2.2.2) Глобальный кэш (GDS) содержит 2 атомика для быстрого доступа.
* (2.1) wave64 (subgroupSize=64) вызывает все инструкции дважды.
* (2.3) CU и WGP режимы работы для work-group. Один WGP может содержать waves с обоими режимами работы, видимо это нужно для лучшей производительности графики при комбинации вершинного шейдера и меш шейдеров.
* (6.2) Некоторые константы заданы специальными константами, например: 0.5, 1.0, 2.0, 4.0, 1.0/(2*Pi).
* (7.9) WMMA инструкция для умножения матриц fp16 16х16.
* (10.4) Есть поддержка 16 битных деривативов. Есть 64 битные атомарные операции над текстурами.
#amd_gpu
* (1.2.2.1) Локальный кэш (LDS) содержит 64 атомика для быстрого доступа в преелах work-group.
* (1.2.2.2) Глобальный кэш (GDS) содержит 2 атомика для быстрого доступа.
* (2.1) wave64 (subgroupSize=64) вызывает все инструкции дважды.
* (2.3) CU и WGP режимы работы для work-group. Один WGP может содержать waves с обоими режимами работы, видимо это нужно для лучшей производительности графики при комбинации вершинного шейдера и меш шейдеров.
* (6.2) Некоторые константы заданы специальными константами, например: 0.5, 1.0, 2.0, 4.0, 1.0/(2*Pi).
* (7.9) WMMA инструкция для умножения матриц fp16 16х16.
* (10.4) Есть поддержка 16 битных деривативов. Есть 64 битные атомарные операции над текстурами.
#amd_gpu
Синхронизации с помощью рендерграфа.
Реализация более простая чем в FG, тут не поддерживается перестановка команд, но и лучше чем в DE, так как совместим с многопоточностью.
Контекст для записи команд.
Работает поверх существующих контекстов, о которых уже рассказывал. Добавленно только отслеживание состояний ресурсов в пределах
Этап планирования.
Точно также создаются батчи, но теперь через builder паттерн, где можно указать какие ресурсы будут использоваться в батче и их начальное/конечное состояние. Если ресурс используется только в одной ГП-очереди (VkQueue), то указывать его не обязательно. Но если ресурс используется в нескольких очередях, то требуется явно добавить его в батч, тогда внутри вставятся все необхрдимые синхронизации.
Для каждого рендер таска также можно указать начальное и конечное состояние ресурса, это позволит оптимизировать синхронизации между тасками.
Данный рендерграф нужен для прототипирования и в случаях, когда проходы рендера задаются в более высокоуровневом коде, например в скриптах. В таких случаях потери от использования рендерграфа не так важны как скорость разработки.
пример: async compute + RG
#blog #engine
Реализация более простая чем в FG, тут не поддерживается перестановка команд, но и лучше чем в DE, так как совместим с многопоточностью.
Контекст для записи команд.
Работает поверх существующих контекстов, о которых уже рассказывал. Добавленно только отслеживание состояний ресурсов в пределах
RenderTask
и автоматическое перемещение их в нужное состояние. Начальное и конечное состояние ресурса это либо дефолтное как в FG, либо задается вручную на этапе планирования рендер графа, иногда это добавляет ненужные синхронизации, но потери на них минимальные, если не приводят к декомпресии рендер таргетов.Этап планирования.
Точно также создаются батчи, но теперь через builder паттерн, где можно указать какие ресурсы будут использоваться в батче и их начальное/конечное состояние. Если ресурс используется только в одной ГП-очереди (VkQueue), то указывать его не обязательно. Но если ресурс используется в нескольких очередях, то требуется явно добавить его в батч, тогда внутри вставятся все необхрдимые синхронизации.
Для каждого рендер таска также можно указать начальное и конечное состояние ресурса, это позволит оптимизировать синхронизации между тасками.
Данный рендерграф нужен для прототипирования и в случаях, когда проходы рендера задаются в более высокоуровневом коде, например в скриптах. В таких случаях потери от использования рендерграфа не так важны как скорость разработки.
пример: async compute + RG
#blog #engine
Презентация с кратким обзором большинства алгоритмов компьютерной графики:
https://www.cl.cam.ac.uk/teaching/1718/AdvGraph/Printable%20(6-up).pdf
Есть все: трассировка, маршинг, SDF, матрицы, АА, постобработка, VR.
https://www.cl.cam.ac.uk/teaching/1718/AdvGraph/Printable%20(6-up).pdf
Есть все: трассировка, маршинг, SDF, матрицы, АА, постобработка, VR.
Выравнивание данных в Vulkan/Metal.
В Vulkan все выравнивания надо получать в рантайме и под каждый девайс.
В Metal выравнивание указано в документации и привязано к GPUFamily, которое дополнительно разделяется на Common/Apple/Mac со своими требованиями к выравниванию.
В движке выравнивания вынесены в DeviceProperties для использования в рантайме и
Это позволяет на этапе компиляции ресурсов и кода рассчитать размер буфера или задать все смещения константами, что упрощает проверки и дает гарантии, что код будет одинаково работать на разных устройствах.
А еще это дает микрооптимизации на вычисление и доступ к памяти, для тех, кто считает такты, но в некоторых случаях расплачиваться приходится большим расходом памяти на ГП.
#blog #engine
В Vulkan все выравнивания надо получать в рантайме и под каждый девайс.
В Metal выравнивание указано в документации и привязано к GPUFamily, которое дополнительно разделяется на Common/Apple/Mac со своими требованиями к выравниванию.
В движке выравнивания вынесены в DeviceProperties для использования в рантайме и
DeviceLimits
для использования в компайлтайме. DeviceLimits
содержит максимальное выравнивание для большинства ГП. В Metal все выравнивания - константы, поэтому тут просто, а для Vulkan эти значения проверяются через vulkan.gpuinfo, а также при старте движка сравниваются с текущими выравниваниями.Это позволяет на этапе компиляции ресурсов и кода рассчитать размер буфера или задать все смещения константами, что упрощает проверки и дает гарантии, что код будет одинаково работать на разных устройствах.
А еще это дает микрооптимизации на вычисление и доступ к памяти, для тех, кто считает такты, но в некоторых случаях расплачиваться приходится большим расходом памяти на ГП.
#blog #engine
Часто вижу в примерах по Vulkan код вида:
Но с тех времен кое-что изменилось - добавили
Правильнее передавать больше флагов:
1. HOST_VISIBLE | HOST_COHERENT - с optFlags.
2. HOST_VISIBLE | HOST_CACHED - без optFlags.
#blog #vk
Это пошло с первых примеров, тогда на дискретных ГП были такие типы памяти:
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
Проверка ошибок.
Метка
Все проверки можно было заменить одной
В итоге, даже в сложных случаях, таких как промисы и корутины, удалось сделать проверку ошибок и корректный выход из функции без использования исключений.
#blog #engine
CHECK_ERR( expr, [opt] result )
- разворачивается в if ( not (expr) ) return result
.CHECK_TE( expr )
- работает только внутри AsyncTask::Run()
, помечает таск как отмененный и выходит из функции. Такой же результат даст и исключение внутри таска.CHECK_PE( expr )
- используется внутри Promise<>
и возвращает специальное значение CancelPromise
, которое помечает промис/таск как отмененный и у зависящих от него промисов выполнится функция, переданная в Promise::Except( fn )
.CHECK_CE( expr )
- используется внутри корутин (coroutine) и также помечает корутину/таск как отмененный.CHECK_THROW( expr, [opt] exception )
- разворачивается в if ( not (expr) ) throw exception
. Чаще всего используется чтобы прокинуть исключение в скрипты.CATCH_ERR( code, [opt] result )
- возвращает 'result', если 'code' бросает исключение. Используется для оборачивания stl вызовов, которые могут бросить исключение.Метка
[opt]
означает опциональный параметр, по-умолчанию возвращается false
или пустой объект {}
.Все проверки можно было заменить одной
CHECK_THROW
, но исключения дают меньшую производительность, что не так важно, так как случается редко. Также хочется оставить возможность отказаться от исключений, поэтому используются специализированные макросы. Например wasm
по-умолчанию выключает исключения при -O1
и выше для наилучшей производительности.В итоге, даже в сложных случаях, таких как промисы и корутины, удалось сделать проверку ошибок и корректный выход из функции без использования исключений.
#blog #engine
HDR режим монитора.
В Vulkan один из распространенных форматов на мониторе - RGBA16F + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR.
В документации про него нет информации, но удалось найти аналогичный формат в EGL, для которого есть документация: EGL_EXT_gl_colorspace_scrgb_linear.
В нем сказано, что этот формат обратно совместим с sRGB на диапазоне (0,1), но также позволяет использовать и значения более 1.
Цвет (1.0, 1.0, 1.0) соответствует яркости в 80 нит, что очень мало, зато доступен диапазаон до 125.0, что будет соответствовать 10 000 нит.
У меня монитор на 1000 нит и экспериментально выянилось, что цвета сохраняются до 12.0-13.0, после чего плавно уходят в белый цвет. Чисто белый появляется после 500.0.
Какие преимущества это дает:
* намного больший диапазон цветов.
* точное управление яркостью, то есть 1000 нит в игре будет в реальности светить в игрока на 1000 нит.
* можно отказаться от тонемапинга, если не использовать значения за пределами 500.0 или если нужно выводить физически корректную картинку.
Есть и минусы:
* нужно знать яркость монитора для правильного тонемапинга или экспозиции.
* много разных HDR форматов и под каждый нужно настраивать рендер.
* скриншоты от сторонних программ чиают только диапазон (0,1).
Поддержка в игрых и программах.
За 2 года пользования монитором я ни разу не смог воспользоваться HDR режимом. В играх этот режим помечался как неподдерживаемый, возможно больше рассчитывали на HDR TV под консоли, где другой режим.
Только в Doom Enternal есть возможность включить HDR, но картинка была в диапазоне (0,1), а это очень тусклые цвета, то есть они это не тестировали.
Без HDR режима монитор сам подстраивал яркость, что давало хорошую имитацию HDR.
#HDR
В Vulkan один из распространенных форматов на мониторе - RGBA16F + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR.
В документации про него нет информации, но удалось найти аналогичный формат в EGL, для которого есть документация: EGL_EXT_gl_colorspace_scrgb_linear.
В нем сказано, что этот формат обратно совместим с sRGB на диапазоне (0,1), но также позволяет использовать и значения более 1.
Цвет (1.0, 1.0, 1.0) соответствует яркости в 80 нит, что очень мало, зато доступен диапазаон до 125.0, что будет соответствовать 10 000 нит.
У меня монитор на 1000 нит и экспериментально выянилось, что цвета сохраняются до 12.0-13.0, после чего плавно уходят в белый цвет. Чисто белый появляется после 500.0.
Какие преимущества это дает:
* намного больший диапазон цветов.
* точное управление яркостью, то есть 1000 нит в игре будет в реальности светить в игрока на 1000 нит.
* можно отказаться от тонемапинга, если не использовать значения за пределами 500.0 или если нужно выводить физически корректную картинку.
Есть и минусы:
* нужно знать яркость монитора для правильного тонемапинга или экспозиции.
* много разных HDR форматов и под каждый нужно настраивать рендер.
* скриншоты от сторонних программ чиают только диапазон (0,1).
Поддержка в игрых и программах.
За 2 года пользования монитором я ни разу не смог воспользоваться HDR режимом. В играх этот режим помечался как неподдерживаемый, возможно больше рассчитывали на HDR TV под консоли, где другой режим.
Только в Doom Enternal есть возможность включить HDR, но картинка была в диапазоне (0,1), а это очень тусклые цвета, то есть они это не тестировали.
Без HDR режима монитор сам подстраивал яркость, что давало хорошую имитацию HDR.
#HDR
Фото монитора.
Выводится чистый красный цвет от 0 до 1000.
UI выводится в диапазоне (0,1) и выглядит очень тускло.
Формат: RGBA16F + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR.
#HDR
Выводится чистый красный цвет от 0 до 1000.
UI выводится в диапазоне (0,1) и выглядит очень тускло.
Формат: RGBA16F + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR.
#HDR
HDR color grading and display in Frostbite
Как переделали Frostbite для поддержки множества HDR дисплеев.
#HDR
Как переделали Frostbite для поддержки множества HDR дисплеев.
#HDR
Vulkanised 2023
Выложили видео с конференции.
Доклады выглядят узкоспециализированно, поэтому пользы не много.
#news
Выложили видео с конференции.
Доклады выглядят узкоспециализированно, поэтому пользы не много.
#news