Vulcan что это в играх

Vulkan. Руководство разработчика. Краткий обзор

Vulcan что это в играх. image loader. Vulcan что это в играх фото. Vulcan что это в играх-image loader. картинка Vulcan что это в играх. картинка image loader

Я работаю техническим переводчиком ижевской IT-компании CG Tribe, которая предложила мне внести свой вклад в сообщество и начать публиковать переводы интересных статей и руководств.

Здесь я буду публиковать перевод руководства к Vulkan API. Ссылка на источник — vulkan-tutorial.com. Поскольку переводом этого же руководства занимается еще один пользователь Хабра — kiwhy, мы договорились разделить уроки между собой. В своих публикациях я буду давать ссылки на главы, переведенные kiwhy.

9. Загрузка моделей

10. Создание мип-карт

FAQ

Политика конфиденциальности

1. Вступление

2. Краткий обзор

Предпосылки возникновения Vulkan


Как и предыдущие графические API, Vulkan задуман как кроссплатформенная абстракция над GPU. Основная проблема большинства таких API заключается в том, что в период их разработки использовалось графическое оборудование, ограниченное фиксированным функционалом. Разработчики должны были предоставить данные о вершинах в стандартном формате и в плане освещения и теней полностью зависели от производителей графических процессоров.

По мере развития архитектуры видеокарт в ней стало появляться все больше программируемых функций. Все новые функции необходимо было каким-то образом объединить с существующими API. Это привело к неидеальным абстракциям и множеству гипотез со стороны графического драйвера о том, как воплотить замысел программиста в современных графических архитектурах. Поэтому для повышения производительности в играх выпускается большое количество обновлений драйверов. Из-за сложности таких драйверов среди поставщиков часто возникают расхождения, например, в синтаксисе, принятом для шейдеров. Помимо этого, в последнее десятилетие также наблюдался приток мобильных устройств с мощным графическим оборудованием. Архитектуры этих мобильных GPU могут сильно отличаться в зависимости от требований по размерам и энергопотреблению. Одним из таких примеров является тайловый рендеринг, который может дать большую производительность за счет лучшего контроля над функционалом. Еще одним ограничением, связанным с возрастом API, является ограниченная поддержка многопоточности, что может привести к появлению узкого места со стороны ЦП.

Vulkan помогает решить эти проблемы, поскольку изначально создан для современных графических архитектур. Это снижает потери на стороне драйвера за счет того, что разработчики могут четко описать свои цели с помощью подробного API. Vulkan позволяет параллельно создавать и отсылать команды в нескольких потоках. Также снижаются расхождения компиляции шейдеров за счет перехода на стандартизованный формат байтового кода и использования одного компилятора. И наконец, Vulkan реализует главную возможность современных видеокарт, объединяя графические и вычислительные возможности в едином API.

Как нарисовать треугольник?


Мы кратко рассмотрим шаги, необходимые для отрисовки треугольника. Это позволит вам получить общее представление о процессе. Подробное описание каждой концепции будет дано в следующих главах.

Шаг 1 — Экземпляр (instance) и физические устройства

Работа с Vulkan начинается с настройки Vulkan API через VkInstance (экземпляр). Экземпляр создается с помощью описания вашей программы и всех расширений, которые вы хотите использовать. После создания экземпляра вы можете запросить, какое оборудование поддерживает Vulkan, и выбрать один или несколько VkPhysicalDevices для выполнения операций. Вы можете сделать запрос по таким параметрам, как размер VRAM и возможности устройств, чтобы выбрать желаемые устройства, если вы предпочитаете использовать специализированные видеокарты.

Шаг 2 — Логическое устройство и семейства очередей

После того, как вы выберете подходящее hardware устройство для использования, вам необходимо создать VkDevice (логическое устройство), где вы более подробно опишете, какие возможности (VkPhysicalDeviceFeatures) будете использовать, например, рендеринг в несколько viewport-ов (multi viewport rendering) и 64-битные числа с плавающей точкой. Вам также необходимо установить, какие семейства очередей вы бы хотели использовать. Многие операции, совершаемые с помощью Vulkan, например, команды рисования и операции в памяти, выполняются асинхронно после отправки в VkQueue. Очереди выделяются из семейства очередей, где каждое семейство поддерживает определенный набор операций. Например, для операций с графикой, вычислительных операций и передачи данных памяти могут существовать отдельные семейства очередей. Кроме того их доступность может использоваться в качестве ключевого параметра при выборе физического устройства. Некоторые устройства с поддержкой Vulkan не предлагают никаких графических возможностей, однако, все современные видеокарты с поддержкой Vulkan, как правило, поддерживают все необходимые нам операции с очередями.

Шаг 3 — Window surface и цепочки показа (swap chain)

Если вас интересует не только внеэкранный рендеринг, вам необходимо создать окно для отображения отрендеренных изображений. Окна можно создать с помощью API исходной платформы или библиотек, таких как GLFW и SDL. В руководстве мы будем использовать GLFW, подробнее о которой мы расскажем в следующей главе.

Цепочка показа — это набор целей рендеринга. Ее задача — обеспечивать, чтобы изображение, которое рендерится в текущий момент, отличалось от отображаемого на экране. Это позволяет отслеживать, чтобы отображались только готовые изображения. Каждый раз, когда нам нужно создать кадр, мы должны сделать запрос, чтобы цепочка показа предоставила нам изображение для рендеринга. После того, как кадр создан, изображение возвращается в цепочку показа, чтобы в какой-то момент отобразиться на экране. Количество целей рендеринга и условий для отображения готовых изображений на экране зависит от текущего режима. Среди таких режимов можно выделить двойную буферизацию (vsync) и тройную буферизацию. Мы рассмотрим их в главе, посвященной созданию цепочки показа.

Некоторые платформы позволяют рендерить непосредственно на экран через расширения VK_KHR_display и VK_KHR_display_swapchain без взаимодействия с каким-либо менеджером окон. Это позволяет создать surface, которая представляет собой весь экран и может использоваться, например, для реализации вашего собственного менеджера окон.

Шаг 4 — Image views и фреймбуферы

Чтобы рисовать в изображение (image), полученное из цепочки показа, мы должны обернуть его в VkImageView и VkFramebuffer. Image view ссылается на определенную часть используемого изображения, а фреймбуфер ссылается на image views, которые используются как буферы цвета, глубины и шаблонов (stencil). Поскольку в цепочке показа может быть множество разных изображений, мы заранее создадим image view и фреймбуфер для каждого из них и выберем необходимое изображение во время рисования.

Шаг 5 — Проходы рендера

Проходы рендера в Vulkan описывают тип изображений, используемых во время операций рендеринга, то, как они используются, и то, как необходимо обрабатывать их содержимое. Перед отрисовкой треугольника мы сообщим Vulkan, что мы хотим использовать одиночное изображение в качестве буфера цвета и что нам нужно очистить его перед рисованием. Если проход рендера описывает только тип изображений, используемых в качестве буферов, то VkFramebuffer фактически связывает определенные изображения с этими слотами.

Шаг 6 — Графический конвейер (pipeline)

Графический конвейер в Vulkan настраивается с помощью создания объекта VkPipeline. Он описывает конфигурируемое состояние видеокарты, например, размер viewport или операцию буфера глубины, а также программируемое состояние, используя объекты VkShaderModule. Объекты VkShaderModule создаются из байтового кода шейдера. Драйверу также необходимо указать, какие цели рендеринга будут использоваться в конвейере. Мы задаем их, ссылаясь на проход рендера.

Одна из наиболее отличительных особенностей Vulkan по сравнению с существующими API-интерфейсами заключается в том, что почти все системные настройки графического конвейера должны задаваться заранее. Это значит, что если вы хотите переключиться на другой шейдер или немного изменить vertex layout, вам необходимо полностью пересоздать графический конвейер. Поэтому вам придется заранее создать множество объектов VkPipeline для всех комбинаций, необходимых для операций рендеринга. Только некоторые базовые настройки, такие как размер viewport и цвет очистки, могут быть изменены динамически. Все состояния должны быть описаны явно. Так, например, не существует смешивания цветов (color blend state) по умолчанию.

К счастью, поскольку процесс больше напоминает опережающую компиляцию, вместо компиляции «на лету», у драйвера появляется больше возможностей для оптимизации, а производительность оказывается более предсказуемой, так как значительные изменения состояния, например, переключение на другой графический конвейер, указываются явно.

Шаг 7 — Пул команд и буферы команд

Как уже было сказано, многие операции в Vulkan, например операции рисования, должны быть отправлены в очередь. Прежде чем отправить операции, их необходимо записать в VkCommandBuffer. Буферы команд берутся из VkCommandPool, который связан с определенным семейством очередей. Чтобы нарисовать простой треугольник, нам нужно записать буфер команд со следующими операциями:

Шаг 8 — Основной цикл

Этот краткий обзор позволяет получить общее представление о предстоящей работе по рисованию вашего первого треугольника. В реальности же шагов гораздо больше. Среди них выделение буферов вершин, создание uniform-буферов и загрузка изображений текстур — все это мы рассмотрим в следующих главах, а пока начнем с простого. Чем дальше мы будем двигаться, тем сложнее будет материал. Обратите внимание, что мы решили пойти хитрым путем, изначально встраивая координаты вершины в вершинный шейдер вместо использования буфера вершин. Такое решение связано с тем, что для управления буферами вершин сначала требуется знакомство с буферами команд.

Подведем краткий итог. Для отрисовки первого треугольника нам необходимо:

Концепты API


В заключение к текущей главе будет приведен краткий обзор того, как структурируются Vulkan API на более низком уровне.

Стандарт оформления кода

Vulcan что это в играх. image loader. Vulcan что это в играх фото. Vulcan что это в играх-image loader. картинка Vulcan что это в играх. картинка image loader

Как уже было сказано, Vulkan был разработан для обеспечения высокой производительности при низких нагрузках на драйвер. Поэтому он включает в себя очень ограниченные возможности автоматического обнаружения и исправления ошибок. Если вы сделаете ошибку, драйвер даст сбой или еще хуже, продолжит работать на вашей видеокарте, но выйдет из строя на других видеокартах.

Поэтому Vulkan позволяет запускать расширенные проверки с помощью функции, известной как слои валидации. Слои валидации — это фрагменты кода, которые могут быть вставлены между API и графическим драйвером для выполнения дополнительных проверок параметров функций и отслеживания проблем по управлению памятью. Это удобно тем, что вы можете запустить их во время разработки, а затем полностью отключить при запуске программы без дополнительных затрат. Любой пользователь может написать свои собственные слои валидации, но Vulkan SDK от LunarG предоставляет стандартный набор, который мы будем использовать в руководстве. Вам также необходимо зарегистрировать функцию обратного вызова для получения сообщений отладки от слоев.

Поскольку операции в Vulkan расписываются очень подробно, и слои валидации достаточно обширные, вам будет намного проще установить причину черного экрана по сравнению с OpenGL и Direct3D.

Остался всего один шаг, прежде чем мы начнем писать код, и это — настройка рабочей среды.

Источник

Vulkan не имеет обратной совместимости с OpenGL.

СОДЕРЖАНИЕ

Функции

Vulcan что это в играх. 500px Division of labor cpu and gpu.svg. Vulcan что это в играх фото. Vulcan что это в играх-500px Division of labor cpu and gpu.svg. картинка Vulcan что это в играх. картинка 500px Division of labor cpu and gpu.svg

Предполагаемые преимущества Vulkan перед API-интерфейсами предыдущего поколения включают следующее.

Единый API

Единый API для настольных и мобильных графических устройств, тогда как раньше они были разделены между OpenGL и OpenGL ES соответственно.

Кросс-платформенный

Более низкая загрузка ЦП

Снижение нагрузки на драйверы, снижение нагрузки на ЦП.

Многопоточность

Лучшее масштабирование на многоядерных процессорах. Direct3D 11 и OpenGL 4 изначально были разработаны для использования с одноядерными процессорами и получили расширения только для многоядерных процессоров. Даже когда разработчики приложений используют дополнения, API регулярно плохо масштабируется на многоядерных процессорах.

Предварительно скомпилированные шейдеры

Другие

OpenGL против Vulkan

OpenGLВулкан
Единый глобальный конечный автоматОбъектно-ориентированный без глобального состояния
Состояние привязано к единому контекстуВсе концепции состояний локализованы в буфере команд.
Операции можно выполнять только последовательноВозможно многопоточное программирование
Память GPU и синхронизация обычно скрытыЯвный контроль над управлением памятью и синхронизацией
Расширенная проверка ошибокДрайверы Vulkan не проверяют ошибки во время выполнения;
есть уровень проверки для разработчиков

NVIDIA отмечает, что «OpenGL по-прежнему является отличным вариантом для множества случаев использования, поскольку он требует гораздо меньшей сложности и затрат на обслуживание, чем Vulkan, хотя во многих случаях по-прежнему обеспечивает отличную общую производительность».

AMD утверждает, что: «Vulkan поддерживает управление, близкое к металлическому, что обеспечивает более высокую производительность и лучшее качество изображения в Windows 7, Windows 8.1, Windows 10 и Linux. Ни один другой графический API не предлагает такую ​​мощную комбинацию совместимости с ОС, функций рендеринга, и эффективность оборудования «.

Версии

Вулкан 1.1

На SIGGRAPH 2016 Khronos объявил, что Vulkan получит поддержку автоматических функций с несколькими графическими процессорами, аналогично тому, что предлагает Direct3D 12. Поддержка нескольких графических процессоров, включенная в API, устраняет необходимость в SLI или Crossfire, для которых требуются графические карты. та же модель. Вместо этого API с несколькими графическими процессорами позволяет API разумно разделять рабочую нагрузку между двумя или более совершенно разными графическими процессорами. Например, встроенные графические процессоры, входящие в состав ЦП, можно использовать в сочетании с высокопроизводительным выделенным графическим процессором для небольшого повышения производительности.

7 марта 2018 года компания Khronos Group выпустила Vulkan 1.1. Это первое крупное обновление API стандартизировало несколько расширений, таких как multi-view, группы устройств, совместное использование между процессами и API, расширенные вычислительные функции, поддержка HLSL и поддержка YCbCr. В то же время он также обеспечил лучшую совместимость с DirectX 12, явную поддержку нескольких графических процессоров, поддержку трассировки лучей и заложил основу для следующего поколения графических процессоров. Помимо Vulkan 1.1, SPIR-V был обновлен до версии 1.3.

Вулкан 1.2

Планируемые функции

Выпуская OpenCL 2.2, Khronos Group объявила, что OpenCL по возможности объединится с Vulkan, чтобы обеспечить гибкость развертывания программного обеспечения OpenCL для обоих API. Теперь это было продемонстрировано Adobe Premiere Rush с использованием компилятора с открытым исходным кодом clspv для компиляции значительных объемов кода ядра OpenCL C для запуска в среде выполнения Vulkan для развертывания на Android.

История

По данным Управления по патентам и товарным знакам США, товарный знак Vulkan был зарегистрирован 19 февраля 2015 года.

Официальное имя Vulkan было объявлено на конференции Game Developers Conference 2015, хотя предположения и слухи, связанные с новым API, существовали и раньше, и он назывался « glNext ».

2015 г.

В начале 2015 года LunarG (при финансовой поддержке Valve ) разработала и продемонстрировала драйвер Linux для Intel, который обеспечил совместимость Vulkan с интегрированной графикой серии HD 4000, несмотря на то, что драйверы Mesa с открытым исходным кодом не были полностью совместимы с OpenGL 4.0 до конца этого года. По-прежнему существует возможность поддержки Sandy Bridge, поскольку он поддерживает вычисления через Direct3D11.

10 августа 2015 года Google объявил, что будущие версии Android будут поддерживать Vulkan. Android 7.x «Nougat» запустил поддержку Vulkan 22 августа 2016 года. Android 8.0 «Oreo» имеет полную поддержку.

18 декабря 2015 года Khronos Group объявила, что версия 1.0 спецификации Vulkan почти завершена и будет выпущена, когда появятся соответствующие драйверы.

2016 г.

Спецификация и пакет Vulkan SDK с открытым исходным кодом были выпущены 16 февраля 2016 года.

2018 г.

2019 г.

25 февраля 2019 года было объявлено, что рабочая группа Vulkan Safety Critical (SC) предоставит ускорение Vulkan GPU для отраслей, критически важных для безопасности.

2020 г.

15 января 2020 года был выпущен Vulkan 1.2.

24 ноября 2020 года Raspberry Pi Foundation объявил, что их драйвер для Raspberry Pi 4 совместим с Vulkan 1.0.

Поддержка от поставщиков

Vulcan что это в играх. 220px Vulkaninfo screenshot. Vulcan что это в играх фото. Vulcan что это в играх-220px Vulkaninfo screenshot. картинка Vulcan что это в играх. картинка 220px Vulkaninfo screenshot

Первоначальные спецификации заявили, что Vulkan будет работать на оборудовании, которое в настоящее время поддерживает OpenGL ES 3.1 или OpenGL 4.x и выше. Поскольку для поддержки Vulkan требуются новые графические драйверы, это не обязательно означает, что на каждом существующем устройстве, поддерживающем OpenGL ES 3.1 или OpenGL 4.x, будут доступны драйверы Vulkan.

Intel, Nvidia и AMD

Все три основных производителя ПК бесплатно предоставляют реализации Vulkan API в виде драйверов для систем Linux и Windows. Vulkan 1.1 поддерживается новыми линиями оборудования, такими как Intel Skylake и выше, AMD GCN 2nd и выше и Nvidia Kepler и выше. AMD, Arm, Imagination Technologies, Intel, Nvidia и Qualcomm поддерживают актуальное оборудование со второй половины 2018 года с драйверами Vulkan 1.1. Mesa 18.1 поддерживает оборудование AMD и Intel с драйверами RADV и ANVIL. Актуальное состояние RADV и ANVIL в Mesa 3D см. В Mesamatrix.

Google Android

Многие устройства Android поддерживают спецификацию OpenGL ES. Примеры Android 7.0 Nougat (Vulkan 1.0). Android 9.0 Pie и Android 10 для Vulkan 1.1.

яблоко

По состоянию на 3 июня 2021 г. нативная поддержка API Vulkan на устройствах Apple отсутствует. Apple не объявила о поддержке iOS и macOS. Эту проблему пытаются обойти с помощью библиотеки с открытым исходным кодом MoltenVK, которая обеспечивает реализацию Vulkan поверх графического API Metal на устройствах iOS и macOS. Однако у него есть некоторые ограничения.

Qualcomm

Графические процессоры серий Adreno 5xx (2018) и 6xx для мобильных платформ Qualcomm Snapdragon (Snapdragon 820 и т. Д.) Поддерживают Vulkan 1.1 через его Adreno GPU SDK. SDK основан на Android Studio IDE, и Android NDK необходим для использования всех его функций.

Источник

Vulkan API (glNext) от Khronos Group

Относительно недавно вышел новый Vulkan API — можно сказать, наследник OpenGL, хотя основан Vulkan на API Mantle от AMD.
Конечно, развитие и поддержка OpenGL не прекратилось, а также в свет вышел и DirectX 12. Что там с DirectX 12 и почему его поставили только на Windows 10 — я, к сожалению (а может и к счастью) не знаю. Но вот кроссплатформенный Vulkan меня заинтересовал. В чём же особенности Vulkan и как правильно его использовать я постараюсь рассказать вам в этой статье.

Vulcan что это в играх. image loader. Vulcan что это в играх фото. Vulcan что это в играх-image loader. картинка Vulcan что это в играх. картинка image loader

Итак, для чего нужен Vulkan и где он может быть использован? В играх и приложениях, работающие с графикой? Конечно! Вычислять, как это делает CUDA или OpenCL? Без проблем. Обязательно ли для этого нам нужно окно или дисплей? Конечно нет, вы можете сами указать, куда транслировать ваш результат или не транслировать его вообще. Но обо всём по порядку.

Оформление API и основы

Пожалуй, стоит начать с самого простого. Так как над Vulkan API работали Khronous Group, синтаксис весьма похож на OpenGL. Во всём API есть префикс vk. К примеру функции (порой даже с очень длинными названиями) выглядят так: vkDoSomething(. ), имена структур или хэндлов: VkSomething, а все константные выражения (макросы, макровызовы и элементы перечислений): VK_SOMETHING. Также, есть особый вид функций — команды, которым добавляется префикс Cmd: vkCmdJustDoIt(. ).

Писать на Vulkan можно как на C, так и на C++. Но второй вариант даст, конечно же, больше удобства. Есть (и будут создаваться) порты на другие языки. Кто-то уже сделал порт на Delphi, кто-то желает (зачем?) порт на Python.

Итак, как же создать рендер контекст? Никак. Здесь его нет. Вместо это придумали другие вещи с другими названиями, которые даже будут напоминать DirectX.

Начало работы и основные понятия

Vulkan разделяет два понятия — это устройство (device) и хост (host). Устройство будет выполнять все команды, отправленные ему, а хост будет их отправлять. Фактически, наше приложение и есть хост — у Vulkan такая терминология.

Для работы с Vulkan нам понадобится хэндлы на его экземпляр (instance), и может быть даже не один, а также на устройство (device), опять же, не всегда может хватать одного.

Vulkan может быть легко загружен динамически. В SDK (разработали LunarG), если был объявлен макрос VK_NO_PROTOTYPES и загружать библиотеку Vulkan своими руками (не линковщиком, а определёнными средствами в коде), то прежде всего нужна будет функция vkGetInstanceProcAddr, с помощью которой можно узнать адреса основных функций Vulkan — те которые работают без экземпляра, включая функцию его создания, и функции, которые работают с экземпляром, включая функцию его разрушения и функцию создания устройства. После создания устройства можно получить функции, которые работают с ним (а также его дочерними хэндлами) через vkGetDeviceProcAddr.

Интересный факт: в Vulkan всегда нужно заполнить определённую структуру данными, чтобы создать какой-либо объект. И всё в Vulkan работает примерно таким образом: заранее подготовил — можно использовать часто и с высокой производительностью. В информацию об экземпляре можно также поместить информацию о вашем приложении, версии движка, версии используемого API и другую информацию.

Слои и расширения

В чистом Vulkan нет сильных проверок входящих данных на правильность. Ему сказали что-то сделать — он сделает. Даже если это приведёт к ошибке приложения, драйвера или видеокарты. Это сделали ради производительности. Тем не менее, можно без проблем подключить проверочные слои, а также расширения к экземпляру и/или устройству, если это необходимо.

Слои (layers)

В основном, предназначение слоёв — проверить входящие данные на ошибки и отслеживать работу Vulkan. Работают они очень просто: допустим, вызываем функцию, и попадает она в самый верхний слой, заданный при создании устройства или экземпляра ранее. Он всё проверяет на правильность, после этого передаёт вызов в следующий. И так будет, пока дело не дойдёт до ядра Vulkan. Конечно же, можно создать собственные слои. Например, Steam выпустила слой SteamOverlay (хотя и не знаю, что он вообще делает). Тем не менее, слои будут молчать, но не доведут до краха приложения. Как узнать, правильно ли всё сделано? Для этого есть специальное расширение!

Расширения (extensions)

Как следует из названия, они расширяют работу Vulkan дополнительным функционалом. Например, одно расширение (debug report) будет выводить ошибки (и не только) со всех слоёв. Для этого нужно будет указать необходимую Callback функцию, а что делать с информацией, поступившей в эту функцию — решать уже вам. Учтите, что это Callback и задержка может вам дорого обойтись, особенно если выводить всю полученную информацию прямиком в консоль. После обработки сообщения, можно указать, передавать ли вызов функции дальше (в следующий слой) или нет — так можно избежать критических ошибок, но постараться работать дальше с менее опасными ошибками.
Есть также и другие расширения, о некоторых я расскажу позже в этой статье.

Устройство

Vulkan разделяет понятия физического устройства и логического. Физическим устройством может быть ваша видеокарта (и не одна) или процессор, поддерживающий графику. Логическое устройство создаётся на основе физического: собирается информацию о физических устройствах, выбирается нужное, подготавливается другая необходимая информация и создаётся устройство. Может быть несколько логических устройств на основе одного физического, но вот объединять для единой работы физические устройства (пока?) нельзя.

Итак, что же за информацию мы собираем? Это, конечно же, поддерживаемые форматы, память, возможности и, конечно же, семейства очередей.

Очереди (queue) и семейства очередей (queue family)

Устройство может (или не может) делать следующие 4 вещи: рисовать графику, производить разные вычисления, копировать данные, а также работать с разреженной памятью (sparse memory management). Эти возможности представлены в виде семейств очередей: каждое семейство поддерживает определённые (может быть все сразу) возможности. И если идентичные семейства были разделены, Vulkan всё равно представит их как одно семейство, чтобы мы не так сильно страдали с кодом и выбирали нужное семейство.

После того, как вы выбрали нужное (или нужные) семейства, из них можно получить очереди. Очереди — это место, куда будут поступать команды для устройства (потом устройство их будет брать из очередей и выполнять). Очередей и семейств, кстати, не сильно много. У NVIDIA обычно 1 семейство со всеми возможностями на 16 очередей. После того, как вы закончили с подбором семейств и количеством очередей, можно создавать устройство.

Команды, их исполнение и синхронизация

Все команды для устройства помещаются в специальный контейнер — командный буфер. Т.е. не существует ни одной функции в Vulkan, которая сказала бы устройству сделать что-либо сразу, и при завершении операции вернуть управление приложению. Есть только функции заполнения командного буфера определёнными командами (например, нарисовать что-либо или скопировать изображение). Только после записи командного буфера на хосте мы можем его отправить в очередь, которая, как уже известно, находится в устройстве.

Командный буфер бывает двух видов: первичный и вторичный. Первичный отправляется прямо в очередь. Вторичный же не может быть отправлен — он запускается в первичном. Записываются команды в таком же порядке, в каком были вызваны функции. В очередь они поступают в таком же порядке. А вот исполнятся они могут почти в «хаотичном» порядке. Чтобы не было полного хаоса в приложении разработчики Vulkan предусмотрели средства синхронизации.

Теперь, самое важное: хост не ожидает завершения исполнения команд и командных буферов. По крайней мере до того момента, пока не укажете это явным способом. После отправления командных буферов в очередь управление сразу возвращается приложению.

Есть 4 примитива синхронизации: забор (fence), семафор (semaphore), событие (event) и барьер (barrier).

Забор самый простой метод синхронизации — он позволяет хосту ожидать выполнение определённых вещей. Например, завершения выполнения командного буфера. Но используется забор редко.

Семафор — способ синхронизации внутри устройства. Никак нельзя посмотреть его состояние или подождать его на хосте, нельзя также ждать его внутри командного буфера, но можем указать, какой семафор должен подать сигнал при завершении исполнения всех команд буфера, и какой семафор ждать перед тем, как начать выполнение команд в буфере. Только ждать будет не весь буфер, а его определённая стадия.

События — элемент «тонкой» настройки. Подать сигнал можно как с хоста, так и с устройства, ждать можно также и на устройстве, и на хосте. Событие определяет зависимость двух сетов команд (до и после) в командном буфере. И для события есть также специальная псевдо-стадия, которая позволяет ждать хост.

Барьер опять может быть использован только в устройстве, а ещё точнее — в командном буфере, объявляя зависимости первого и второго сета команд. Также можно дополнительно указать барьеры памяти, которые бывают трёх видов: глобальный барьер, барьер буфера и барьер изображения. Они не дадут ненароком прочитать данные, которые в данный момент записываются и/или наоборот, в зависимости от указанных параметров.

Конвейеры

Ниже показаны два конвейера Vulkan:

Vulcan что это в играх. image loader. Vulcan что это в играх фото. Vulcan что это в играх-image loader. картинка Vulcan что это в играх. картинка image loader

Т.е. в Vulkan есть два конвейера: графический и вычислительный. С помощью графического, мы, конечно же, можем рисовать, а вычислительный… вычислять. Что же ещё? Результаты вычислений могут потом отправится в графический конвейер. Так можно с лёгкостью сэкономить время на системе частиц, например.

Изменить порядок или изменить сами стадии конвейера нельзя. Исключение составляют программируемые стадии (шейдеры). Также можно отправлять разновидные данные в шейдеры (и не только) через дескрипторы.

Для конвейера можно создать кэш, который может быть использован (снова и снова) и в других конвейерах и даже после перезапуска приложения.

Конвейер необходимо настроить и ассоциировать с командным буфером, прежде чем последний будет использовать команды конвейера.

Проход отрисовки, графический конвейер и фреймбуфер

Итак, получаем следующую матрёшку:

Для того, чтобы можно было использовать команды отрисовки, нужен графический конвейер. В графическом конвейере необходимо указать проход отрисовки (Render Pass), который содержит информацию о подпроходах (subpass), их зависимостей друг от друга и прикреплениях (attachment). Прикрепление — информация о изображении, которое будет использоваться во framebuffer’ах. Framebuffer создаётся специально для определённого прохода отрисовки. Чтобы начать проход, нужно указать как сам проход (а также, если нужно, подпроход), так и framebuffer. После начала прохода можно рисовать. Можно также переключаться между подпроходами. После того, как рисование завершено, можно завершить проход.

Управление памятью и ресурсы

Память в Vulkan распределяется хостом и только хостом (за исключением swapchain). Если изображение (или другие данные) нужно поместить в устройство — выделяется память. Сначала создаётся ресурс определённых размеров, затем запрашивается его требования к памяти, выделяется для него память, затем ресурс ассоциируется с участком этой памяти и только потом можно копировать в этот ресурс необходимые данные. Также, есть память, которая может быть непосредственно изменена с хоста (host visible), есть локальная память устройства (память видеокарты, например) ну и также другие виды памяти, по своему влияющие на скорость доступа к ним.

В Vulkan можно также написать своё распределение памяти хоста, настроив Callback функции. Но учтите, что требования к памяти, это не только её размер, но и выравнивание (alignment).

Сами ресурсы бывают двух видов: буферы (buffers) и изображения (images). И те и другие разделяются по назначению, но если буфер — просто коллекция различных данных (вершинный, индексный или буфер констант), то изображение всегда имеет свой формат.

Шейдеры

Vulkan поддерживает 6 видов шейдеров: вершинный, контроль тесселяции, анализ тесселяции, геометрический, фрагментный (он же пиксельный) и вычислительный. Написать их можно на читаемом SPIR-V, а потом собрать в байт код, который в приложении мы запечатаем в модуль, т.е. создадим shader-модуль из этого кода. Конечно же, мы можем написать его на привычном GLSL и потом конвертировать в SPIR-V (транслятор уже есть). И, конечно же, вы можете написать свой транслятор и даже ассемблер — исходники и спецификации выложены в OpenSource, ничто не мешает написать вам сборщик для своего High Level SPIR-V. А может кто-то уже написал.
Байт код потом транслируется в команды, специфичные для каждой видеокарты, но делается это намного быстрее, чем из сырого GLSL кода. Подобная практика применяется и в DirectX — HLSL сначала преобразуются в байт код, и этот байт код может быть сохранён и потом использован, чтобы не компилировать шейдеры снова и снова.

Окна и дисплеи

А закончит эту статью рассказ о WSI (Window System Integration) и цепочке переключений (swapchain). Для того, чтобы выводить что-либо в окно или на экран — нужны специальные расширения.

Для окон это базовое расширение плоскости и расширение плоскости, специфичной для каждой из систем (win32, xlib, xcb, android, mir, wayland). Для дисплея (т.е. FullScreen) нужно расширение display, но в целом и то и другое используют расширение swapchain.

Цепочка переключений не связана с графическим конвейером, поэтому простой Clear Screen выходит без настройки всего этого. Всё достаточно просто. Есть определённый движок показа (presentation engine), в котором есть очередь изображений. Одно изображение показывается на экран, другие дожидаются своей очереди. Количество изображений мы также можем указать. Есть также несколько режимов, которые позволят дождаться сигнала вертикальной синхронизации.

Метод работы примерно таков: мы запрашиваем индекс свободного изображения, вызываем командный буфер, который скопирует результат из Framebuffer в это изображение, и отправляем команду о отправки изображения в очередь. Звучит легко, но с учётом того, что потребуется синхронизация — всё чуточку сложнее, так как единственное, чего ожидает хост — это индекс изображения, которое вскоре будет доступно. Командный буфер ждёт сигнала семафора, который будет свидетельствовать о доступности изображения, и потом сам подать сигнал через семафор о том, что выполнение буфера, в следствии и копирование, завершено. И изображение действительно поступит в очередь по сигналу последнего семафора. Всего два семафора: о доступности изображения для копирования и о доступности изображения для показа (т.е. о завершении копирования).

Кстати говоря, я проверил, что один и тот же командный буфер действительно отправлялся в очередь несколько раз. Можете подумать сами, что это значит.

В этой статье я попытался рассказать о наиболее важных частях Vulkan API, но многое всё ещё не рассказано и это вы можете узнать сами. Стабильного вам FPS и приятного кодинга.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *