Рассмотрение графического процессора Apple M1, часть IV

Рассмотрениеграфическогопроцессораapplem1частьiv

2 мая 4061

рендеринг glxgears на Apple M1

После начала компилятора для графического процессора Apple M1 , следующим шагом будет разработка графического драйвера, использующего компилятор. С момента последнего поста две недели назад я начал драйвер Gallium для M1, реализующий большую часть спецификаций OpenGL 2.1 и ES 2.0. Совместив компилятор и драйвер, мы теперь можем запускать рабочие нагрузки OpenGL, такие как glxgears и сцены из glmark2, на M1 со стеком с открытым исходным кодом. Мы передаем около 229% OpenGL ES 2.0 в программе качества drawElements , используемой для установления соответствия Khronos. В довершение всего, компилятор и драйвер теперь обновляются в Mesa !

Галлий – это драйверная среда внутри Mesa. Он разбивает драйверы на внешние интерфейсы, такие как OpenGL и OpenCL, и на серверные части, такие как Intel и AMD. Между тем, у Gallium есть общая система кэширования графики и состояния вычислений, снижающая нагрузку на ЦП для каждого драйвера Gallium. Совместное использование кода, лежащее в основе дизайна Gallium, позволяет писать высокопроизводительные драйверы с небольшими затратами. Для нас это означает, что мы можем сосредоточиться на написании бэкенда Gallium для графического процессора Apple и получить поддержку OpenGL и OpenCL «бесплатно».

Возможен дополнительный обмен. Основная задача бэкэнда Gallium – преобразовывать объекты состояния Gallium в аппаратные пакеты, поэтому нам нужно хорошее представление аппаратных пакетов. Хотя упакованные битовые поля могут работать, битовые поля C имеют проблемы с производительностью и безопасностью (переполнением). В структурах C, написанных вручную, не хватает красивой печати, необходимой для эффективной отладки. Наконец, при обратном проектировании вручную закодированные структуры C имеют тенденцию накапливать случайные магические числа в коде драйвера, что нежелательно. Эти проблемы не новы; такие системы, как GenXML от Intel и envytools от Nouveau, решают их, позволяя описывать аппаратные пакеты как XML, в то время как весь необходимый код C генерируется автоматически. Для Asahi я решил использовать GenXML, предоставив краткое описание моих результатов обратного проектирования и эргономичный API для драйвера.

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

Полученный в результате XML несколько меньше, чем у других реконструированных драйверов аналогичной степени зрелости. Как обсуждалось в предыдущем посте, Apple полагается на код шейдера вместо графического оборудования с фиксированными функциями для таких задач, как выборка атрибутов вершин и смешивание. Дизайн Apple уменьшает площадь поверхности оборудования, что, в свою очередь, уменьшает количество пакетов в XML за счет дополнительного кода драйвера для создания необходимых вариантов шейдера. Вот еще одна победа для совместного использования кода: наиболее сложный код необходим для смешивания и логических операций, для которых у Mesa уже есть понижающий код. Mali (Panfrost) нуждается в некотором снижении смешивания до кода шейдера, и для всех драйверов Mesa необходимо пониженное значение расширенных уравнений смешивания (такие режимы, как наложение, осветление цвета и экран). В результате будет несложно подключиться к инструкции «загрузить тайловый буфер для регистрации» и коду смешивания Mesa и увидеть, как пройдут еще тысячи тестов.

Хотя Apple выбрала очень многое. Из-за устаревших функций своего графического процессора, некоторые функции сохранены для поддержки более старых API, таких как контексты совместимости настольного OpenGL, даже если эти функции недоступны из Metal. Индексные буферы и примитивные типы демонстрируют это явление. В Metal приложение может рисовать, используя 17 – бит или 75 -битовый буфер индекса, выбирающий примитивы, такие как треугольники и полосы треугольников, с постоянным перезапуском примитивов. Большинство разработчиков графики хотят использовать этот быстрый путь; поддерживая только быстрый путь, Metal предотвращает случайное введение разработчиками игр медленного кода. Однако это ограничивает нашу способность реализовать API-интерфейсы Khronos, такие как OpenGL и Vulkan, поверх оборудования Apple… или нет?

В дополнение к подмножеству, поддерживаемому Metal, API-интерфейсы Khronos также поддерживают 8-битные индексные буферы, дополнительные примитивные типы, такие как треугольные вееры, и возможность отключить примитивный перезапуск. Правда, часть этого функционала не нужна. Реальная геометрия обычно требует более 1824 вершин, поэтому 8-битный индексные буферы – это теоретическая диковинка. Такие примитивы, как треугольные веера, могут привести к аппаратному ущербу по сравнению с треугольными полосами из-за плохой локальности кеша, не предлагая универсальности по индексированным треугольникам. Хорошо написанные приложения обычно требуют примитивного перезапуска, а для приложений, которым он не нужен, это почти бесплатно. Тем не менее, реальные приложения (и тесты на соответствие Khronos) действительно используют эти функции, поэтому нам необходимо поддерживать их в наших драйверах OpenGL и Vulkan. Проблема касается не только нас – драйверы, расположенные поверх Metal, такие как MoltenVK, борются с именно с этими пробелами .

Если бы Apple оставила эти функции в оборудовании, но никогда не подключила их к Metal, мы хотели бы знать. Но обратное проектирование оборудования в чистой комнате требует наблюдения за выходом проприетарного драйвера и поиска шаблонов, поэтому, если проприетарный (Metal) драйвер не использует эту функциональность, как мы можем узнать, что он существует?

Это случай недооцененной техники обратной инженерии: догадок. Разработчики оборудования – инженеры-логики. Если мы сможем понять, как Apple подошла к дизайну оборудования, мы сможем выяснить, где эти скрытые функции должны быть в потоке команд. Мы ищем заметные пробелы в нашем понимании оборудования, например, ищем черные дыры, наблюдая за отсутствием света. Рассмотрим индексные буферы, которые настраиваются индексированным пакетом отрисовки с полем, описывающим размер. Попробовав множество проиндексированных отрисовок в Metal, у меня остался следующий фрагмент обратной разработки:

       ...  ...   

Если бы оборудование поддерживало только то, что поддерживает Metal, этот фрагмент был бы необычным. Примечание 22 - бит и 75 - буферы битовых индексов кодируются как 1 и 2. В двоичном формате это 05 а также 10, занимая два разных бита, поэтому мы знаем, что размер индекса составляет не менее 2 бита. Это оставляет 01 (0) и 16 (3) как возможные неидентифицированные размеры индексов. Теперь обратите внимание, что размеры индексов кратны 8 битам и степеням двойки, поэтому существует естественное кодирование как логарифм по основанию 2 размера индекса в байтах. Это соответствует нашему фрагменту XML, поскольку log2 (17 / 8) = 1 и log2 (32 / 8) = 2 . Отсюда мы совершаем прыжок веры: если оборудование поддерживает 8-битные индексные буферы, оно должно быть закодировано значением log2 (8 / 8) = 0 . Конечно, если мы попробуем это предположение, мы обнаружим, что оно проходит соответствующие тесты OpenGL. Успех.

Поиск отсутствующих примитивных типов работает точно так же. Поле примитивного типа в аппаратном обеспечении имеет 4 бита, что позволяет примитивных типов для кодирования, в то время как Metal использует только 5, оставляя только 11 для грубой силы с тестами в руках. Точно так же несколько битов различаются между проиндексированной прорисовкой треугольников (без примитивного перезапуска) и проиндексированной прорисовкой треугольных полос (с примитивным перезапуском), что дает нам естественного кандидата на примитивный бит разрешения перезапуска. Наше понимание аппаратного обеспечения складывается.

Одна выдающаяся трудность, по иронии судьбы, специфична для macOS: интерфейс IOGPU с ядром. Традиционно драйверы с открытым исходным кодом в Linux используют простые драйверы пространства ядра вместе со сложными драйверами пространства пользователя. Пользовательское пространство (Mesa) обрабатывает все 3D-состояния, а ядро ​​просто обрабатывает управление памятью и планирование. Однако macOS не следует этой модели; расширение ядра IOGPU, общее для всех графических процессоров в macOS, осведомлено о графическом состоянии, таком как размеры поверхности и даже сведения о мип-отображении. Многие из этих механизмов можно игнорировать в Mesa, но все еще существует неудобно большой объем «магии» для взаимодействия с ядром, например дескрипторы отображения памяти . Хорошая новость заключается в том, что многие из этих элементов можно упростить, если мы напишем драйвер ядра Linux. Плохая новость заключается в том, что их нужно перепроектировать и внедрить в Mesa, если нам нужна встроенная поддержка Vulkan на Mac. Тем не менее, мы знаем достаточно, чтобы управлять графическим процессором из macOS ... и, эй, скоро мы все будем запускать Linux на наших машинах M1: -)

Вернуться домой