Спецификация и драйверы для Vulkan, графического API нового поколения Khronos, как ожидается, будут выпущены в конце этого года. Тем не менее, мы можем начать изучение того, как этот новый API будет работать, изучив проект, из которого он развился: Mantle.

Проблема с API, такими как OpenGL и DirectX 11, заключается в том, что они не являются хорошей абстракцией современного графического оборудования, что приводит к очень сложным драйверам, которые пытаются угадать, что приложение на самом деле хочет выполнить. Mantle - это проект AMD, который решает эти проблемы, предлагая низкоуровневый API и упрощенный драйвер, который не мешает.

Для Mantle нет общедоступного SDK, но AMD выпустила руководство по программированию, и есть различные игры и демонстрации, использующие Mantle, например, Star Swarm Stress Test. Руководство по программированию можно использовать для восстановления заголовка. Демонстрационные примеры загружают точки входа в функции из mantle64.dll, что означает, что легко произвести трассировку вызовов Mantle, создав нашу собственную прокси DLL с реализациями, выглядящими следующим образом:

К счастью, демонстрация Star Swarm начинается с рендеринга основного экрана-заставки с двумя треугольниками, в результате чего получается простая трассировка, показывающая основы использования Mantle. Благодаря этой информации и некоторой помощи других людей мне удалось написать демонстрацию Hello Triangle на C ++ », которая занимает около 560 строк кода. В следующих разделах я рассмотрю этот код и опишу, что нужно для рендеринга цветного треугольника для каждой вершины в Mantle. Некоторый опыт работы с API, такими как DirectX или OpenGL, помогает полностью понять этот пост.

Настраивать

Первое, что нам нужно сделать, это загрузить точки входа в функцию из mantle64.dll. Я создал вспомогательную функцию, которая инициализирует указатели на функции, очень похоже на GLEW для OpenGL, которую вы можете найти в моем mantle.h.

В Mantle есть дополнительный уровень проверки, который проверяет вызовы функций и аргументы и выдает нам предупреждения, когда мы делаем что-то не так. Чтобы получить их, мы должны зарегистрировать обратный вызов отладки:

Как видите, функции Mantle имеют префикс gr. Многие функции Vulkan просто будут иметь одно и то же имя с заменой gr на vk.

Устройства и очереди

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

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

Чтобы использовать графический процессор с Mantle, мы должны получить дескриптор устройства, вызвав grCreateDevice. Mantle улучшает систему расширений OpenGL, требуя от нас явно указывать, какие расширения мы хотим использовать. Вызов функций завершится ошибкой, если вы забудете указать расширение, что значительно затруднит случайное использование расширений.

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

Чтобы дать вам представление о том, насколько обширен уровень проверки, давайте удалим вызов grGetExtensionSupport и посмотрим, что произойдет после того, как мы вызовем grCreateDevice.

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

  • Универсальная очередь
  • Очередь вычислений

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

После создания устройства мы можем запросить дескриптор очереди:

Визуализировать целевое изображение

Чтобы нарисовать что-то в окне, нам нужно создать изображение. Изображения можно использовать в качестве входных данных для рисования, как текстуры, но их также можно использовать в качестве выходных или целевых. Чтобы изображение отображалось в окне после завершения рендеринга кадра, оно также должно быть презентабельным.

Самый простой способ получить презентабельное изображение - это функция… grWsiWinCreatePresentableImage. Префикс WsiWin указывает, что эта функция из расширения Windows.

Изображение будет иметь каналы RGBA (RGB + альфа), и каждый канал сохраняется как нормализованный беззнаковый байт. Мы указываем, что изображение будет использоваться в качестве цветовой цели, что означает, что цветовой вывод операций рендеринга будет сохранен в нем. Ширина и высота такие же, как у окна.

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

Командные буферы

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

Создание командного буфера выглядит так:

Буфер команд очищается каждый раз при вызове grBeginCommand, после чего в него можно добавлять новые команды. Затем вы можете многократно выполнять один и тот же командный буфер. Концепция очень похожа на старую-добрую функцию списков отображения в OpenGL.

Добавим команду для перехода изображения:

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

Чтобы выполнить этот переход, отправьте буфер команд в очередь:

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

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

Обратите внимание, что grQueueSubmit не принимает объекты дескриптора памяти напрямую; они должны быть обернуты в структуры ссылок памяти:

Теперь, когда изображение готово, нам нужно обернуть его в объект color target view, чтобы позже мы могли сказать Mantle, что нужно рисовать на нем.

Графический конвейер

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

Мы рассмотрим следующие этапы:

  • Входной ассемблер (IA): подготавливает ввод данных вершины, при необходимости используя индексный буфер.
  • Вершинный шейдер (VS): обрабатывает каждую вершину для определения положения на экране и других свойств, используемых пиксельным шейдером.
  • Растеризатор (RS): интерполирует вершины для создания фрагментов, пикселей, которые являются частью примитива, например треугольника.
  • Пиксельный шейдер (PS): создает цвет для каждого фрагмента для одной или нескольких цветовых целей (у нас будет 1, изображение обсуждалось выше).
  • Color blender (CB): обрабатывает перекрывающиеся фрагменты для получения окончательного цвета пикселя с использованием альфа-каналов.

Большинство из этих этапов известны как часть конвейера с фиксированной функцией (зеленый). Они не программируются, но вы можете до некоторой степени настроить их поведение.

Как и в случае с другими объектами, создание графического конвейера требует заполнения структуры данными. Начнем с конфигурации сцены с фиксированной функцией.

Ассемблер ввода должен знать, какие примитивы будут отображаться, чтобы сгруппировать вместе вершины, принадлежащие одному и тому же примитиву (например, треугольнику). Интересно отметить, что Mantle изначально поддерживает рендеринг четырехугольников (GR_TOPOLOGY_QUAD_LIST и даже GR_TOPOLOGY_QUAD_STRIP), в отличие от современных OpenGL и DirectX.

Отсечение глубины отключено в состоянии растеризатора, потому что демонстрация не включает тестирование глубины.

Состояние цветового блендера описывает цветовой формат цели, который соответствует презентабельному изображению, которое мы создали ранее. Он также описывает, какая логическая операция используется для сохранения цвета в цели. Например, это может быть XOR'd с текущим сохраненным значением. Мы просто заменим его новым цветом, указав операцию copy.

Шейдеры

Теперь мы настроили этапы с фиксированной функцией. Остается только этапы вершинного шейдера и пиксельного шейдера. Эти этапы полностью программируются путем компиляции шейдерных программ на AMD Intermediate Language. Анализатор шейдеров в комплекте с CodeXL может компилировать программы HLSL в этот формат байтового кода. Это немного сложно использовать, поэтому я написал сценарий Python, который извлекает двоичный код из вывода.

Вершинный шейдер для Mantle выглядит так:

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

Пиксельный шейдер работает точно так же, как обычный шейдер DirectX 11. Он принимает входные данные от вершинного шейдера и записывает цвет в цель.

После того, как эти шейдеры скомпилированы в байт-код AMD IL, их необходимо загрузить в Mantle:

Вызов loadShader здесь - это вспомогательная функция, которая возвращает вектор C ++ с байтами шейдера. Тип шейдера указывать не нужно, поскольку он закодирован в двоичном формате, см. AMD IL Reference.

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

Точно так же указывается пиксельный шейдер:

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

  • Не используется: не требует пояснений.
  • Ресурс шейдера: читаемый буфер (как текстура)
  • БПЛА: представление с неупорядоченным доступом, может использоваться для произвольных операций чтения / записи.
  • Сэмплер: Сэмплер текстуры.
  • Следующий набор дескрипторов: набор вложенных дескрипторов.

Помните, что вершинный шейдер имеет 2 входа буфера в регистрах 0 и 1. Их необходимо описать с помощью слотов дескрипторов:

Пиксельный шейдер не принимает никаких входных и выходных данных, но количество слотов дескрипторов должно быть одинаковым. Вот здесь и пригодится тип дескриптора unused:

Наконец, вся эта тяжелая работа окупается:

Выделение и назначение памяти объектам

К сожалению, мы еще не полностью закончили конвейер. Ранее я упоминал, что Mantle оставляет большую часть обязанностей по управлению памятью вашему приложению. Одна из этих обязанностей - явное выделение памяти для некоторых объектов, а конвейер - один из тех, кому это необходимо.

Во-первых, нам нужно спросить объект конвейера о его требованиях к памяти:

Требования к памяти определяют, сколько памяти потребуется объекту, требования к выравниванию и в каких кучах памяти он будет наиболее предпочтительно находиться. Если объекту не требуется ручное выделение памяти, например буфер команд, сообщаемый размер будет равен 0.

Mantle предлагает множество типов кучи памяти, которые имеют такие свойства, как доступность ЦП, скорость чтения / записи графического процессора, размер кучи и размер страницы. Чтобы выделить память для конвейера, мы запросим свойства кучи, которая ему нужна.

В моем случае конвейер требует 320 байтов, а куча указывает, что размер страницы составляет 64 КБ. Поскольку память должна распределяться по страницам, вы должны выделить эти кучи и использовать их для различных объектов в реальном приложении. Мы поленимся и выделим эти буферы памяти размером со страницу для каждого объекта.

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

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

Этот процесс одинаков для всех объектов, требующих явного выделения памяти, поэтому я выделил его в вспомогательную функцию.

Набор дескрипторов и данные вершин

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

Каждая вершина имеет атрибут позиции float4 и атрибут цвета float4. Я решил объединить их в единый массив без чередования. Как и следовало ожидать, Mantle использует нормализованные координаты устройства (вершинный шейдер никоим образом не преобразует позицию).

Чтобы использовать эти данные, мы должны выделить память графического процессора для их хранения, что очень похоже на буфер вершин в DirectX / OpenGL. На этот раз придется искать подходящую кучу. Поскольку в какой-то момент мы хотим скопировать в него данные из массива, нам нужно искать кучу памяти, которая поддерживает доступ к ЦП. Поиск кучи с этим свойством выглядит так:

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

Чтобы скопировать данные в выделенную память графического процессора, мы можем отобразить их:

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

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

Как и объект конвейера, мы также должны выделить и привязать память к объектам набора дескрипторов.

Для изменения набора дескрипторов используются специальные вызовы begin и end, аналогичные вызовам командного буфера. Разница в том, что они не удаляют существующие привязки.

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

Буфер позиции привязан к слоту 0, и его данные поступают из первых 12 чисел с плавающей запятой в памяти, по 4 числа с плавающей запятой на вершину (указывается форматом канала).

Буфер цвета (слот 1) присоединяется к памяти точно так же, но с другим смещением в выделенной памяти:

Теперь у нас есть набор дескрипторов, который описывает, откуда буферы, указанные в вершинном шейдере, берут свои данные.

Объекты динамического состояния

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

  • MSAA: конфигурация сглаживания с несколькими выборками
  • Область просмотра: область рендеринга, диапазон глубины, ножницы.
  • Состояние смешивания цветов: как цвета смешиваются на основе альфа-каналов.
  • Состояние трафарета глубины: операции с глубиной и трафаретом.
  • Состояние растра: режим заливки и удаление граней.

В отличие от DirectX и OpenGL, где большинство из них имеют состояния по умолчанию, Mantle требует, чтобы вы указали все это явно. Они известны как динамическое состояние, потому что они могут измениться в любое время без необходимости воссоздания графического конвейера.

Установив для счетчика выборок значение 1, MSAA эффективно отключается. Для использования мультисэмплинга необходимо привязать особую цветовую мишень.

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

Состояние наложения цветов сообщает графическому процессору, как объединить цвет и альфа-канал операции рендеринга и текущее значение пикселей в цветовой цели для создания нового значения пикселя. Подробнее об этом можно прочитать здесь.

Операции с трафаретом и глубиной отключены, но Mantle, тем не менее, требует указать допустимую конфигурацию.

Состояние растра определяет, как растеризатор рисует примитивы. Например, режим заливки GR_FILL_WIREFRAME дает следующий результат:

Отбор лиц (режим отсечения) можно использовать, чтобы не отображать примитивы, которые не обращены в камеру, или наоборот. Это часто используется в качестве оптимизации, чтобы не тратить время на рисование обратной стороны объектов. Параметр front face указывает, как определить, обращен ли примитив к камере, в этом случае проверяя, имеют ли вершины на экране порядок по часовой стрелке, как наш 2D-треугольник.

Создав эти объекты, которые определяют динамическое состояние, мы наконец закончили подготовительную работу.

Больше командных буферов

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

Помните, что мы создали изображение для рендеринга, которое было инициализировано в состоянии Presentable in a window. Давайте начнем с того, что сделаем изображение черным с помощью grCmdClearColorImage. Эта команда требует, чтобы изображение было в состоянии clear, поэтому мы начинаем с перехода:

Команда clear также принимает ряд частей, которые необходимо очистить:

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

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

Обещаю, что настало время по-настоящему нарисовать треугольник. Давайте создадим новый командный буфер для хранения команд.

Помните, что в разделе «Буферы команд» цветовой целевой вид был создан из презентабельного изображения. Так же, как представление памяти для данных вершины, это действует как «указатель» на изображение. Используя команду grCmdBindTargets, Mantle получает указание хранить здесь отрисованные пиксели.

Затем настройте тщательно созданное динамическое состояние:

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

Привяжите представления памяти к слотам дескрипторов шейдеров:

Теперь мы сообщили Mantle все, что ему нужно знать о вводе, операциях и выводе для рендеринга. Остается только один шаг:

Аргументы указывают, что необходимо нарисовать 3 вершины, начиная с вершины 0. Последние два аргумента используются для отрисовки экземпляра. Если вы не используете это, установите индекс экземпляра на 0 и счетчик экземпляров на 1, как показано здесь. Поскольку тип примитива (список треугольников) является свойством конвейера, он не указывается как часть команды рисования, в отличие от glDrawArrays в OpenGL.

Важное свойство буферов команд, которое я еще не объяснил, заключается в том, что все состояние, установленное с помощью команд Bind, привязано к командному буферу. Это означает, что если вы создадите отдельный командный буфер с помощью всего лишь grCmdDraw, ничего не произойдет, потому что к нему не привязан конвейер. Это очень полезно, потому что это означает, что ваши операции рисования никогда не будут испорчены неожиданным глобальным состоянием, потому что его нет!

На данный момент изображение все еще находится в состоянии целевого объекта рендеринга, поэтому оно еще не может быть отображено в окне. Это требует еще одного перехода:

Это охватывает все операции по рисованию одного кадра. Обобщить:

  • Преобразование изображения из презентабельного в ясное
  • Очистить изображение до черного
  • Преобразование изображения из очищаемого в пригодное для использования в качестве цели рендеринга
  • Привязать конвейер, дескрипторы и динамическое состояние
  • Нарисуйте треугольник
  • Преобразование изображения из пригодного для использования в качестве цели рендеринга в презентабельное

Это довольно непросто для одного треугольника, но учтите, что для перехода от одного треугольника к большему так же просто, как предоставить больше данных о вершинах и увеличить число в grCmdDraw.

Цикл рендеринга

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

Отрисовка кадра теперь так же проста, как отправка буферов команд в универсальную очередь:

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

Чтобы представить окончательное изображение в окне, вызовите grWsiWinQueuePresent:

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

Создайте ограждение перед входом в основной цикл и измените вызов grQueueSubmit, чтобы сохранить статус выполнения в этом ограждении.

Теперь добавьте вызов grWaitForFences в начале кадра, чтобы дождаться завершения последнего вызова grQueueSubmit. Если его еще не было (случай для первого кадра), он немедленно вернется.

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

Заключение

Уф, чтобы нарисовать именно этот треугольник, потребовалось немало усилий! Давайте проследим все шаги, которые потребовались для этого:

  • Выбор устройства, совместимого с Mantle, и запрос универсальной очереди
  • Создание изображения для рендеринга и его инициализация
  • Создание графического конвейера путем настройки состояния фиксированной функции, присоединения шейдеров к программируемым вершинам и этапам пикселей и описания типа входных данных шейдера с помощью слотов дескрипторов.
  • Выделение памяти для данных вершины и копирование данных в нее
  • Создание набора дескрипторов, который связывает память данных вершин с буферами положения и цвета, используя набор дескрипторов.
  • Создание объектов для настройки динамического состояния
  • Создание 3 командных буферов для: очистки изображения, рисования треугольника и придания изображению презентабельности в окне.
  • Написание основного цикла, который ожидает завершения рендеринга предыдущего кадра, выполняет 3 буфера команд и представляет изображение в окно.

Понятно, что Mantle заставляет вас делать большую часть тяжелой работы, которая ранее выполнялась драйвером для API, таких как DirectX и OpenGL. Так в чем именно преимущества?

  • Работайте, как создание конвейеров, создание буферов команд и рисование, все поддерживает многопоточность.
  • Очень явный контроль над памятью и управлением ресурсами, устраняющий накладные расходы на драйвер, который предугадывает ваши намерения.
  • Существенно упрощенный компилятор шейдеров, который должен поддерживать только байтовый код, что снижает сложность драйвера = меньше ошибок
  • Обширный уровень проверки, который можно полностью отключить в производственной среде для еще большего повышения производительности
  • Никаких запутанных проблем с глобальным состоянием или скрытой производительностью

Vulkan сохранит в себе все эти преимущества, так что это повод для большого энтузиазма. Тем не менее, API столь низкого уровня не для всех. Чтобы действительно добиться прироста производительности, требуется много работы, поэтому я думаю, что большинству людей, которые в настоящее время используют DirectX 11 и OpenGL для создания игр и демонстраций, следует придерживаться этих API. Я ожидаю, что вложения окупятся только для разработчиков продвинутых движков, таких как Unreal Engine и Unity.

Если вы еще не испугались, в Mantle есть куда поиграть. Функциональность буфера команд намного мощнее, чем показано здесь. Например, с помощью grCmdWhile и grCmdEndWhile можно создавать управляющие структуры, такие как циклы while. Мы также полностью пропустили вычислительную часть, которая может взаимодействовать с графикой сложными способами. Вот что вы можете попробовать дальше:

  • Индексированный рендеринг путем сохранения целочисленных индексов в памяти графического процессора и связывания их с grCmdBindIndexData
  • Предоставление матрицы шейдеру и рендеринг модели в 3D
  • Использование изображений и сэмплеров для текстурирования
  • Создание изображения в качестве цели глубины и использование тестирования глубины

Вы можете научиться использовать большинство из них, прочитав Руководство по программированию Mantle и поэкспериментировав. Если вам нравится Rust, вам также стоит заглянуть в this project, цель которого - стать первой оболочкой для Mantle!