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

Галлий

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

Трекер состояния

Для каждого графического API существует средство отслеживания состояния, которое в основном реализует интерфейс State Tracker API. Каждый State Tracker используется всеми существующими драйверами устройств Gallium3D. Итак, скажем, вы вызываете функцию OpenGL, затем эта функция транслируется для вызова интерфейса State Tracker API. Теперь, когда вы кодируете драйверы устройств, вам не нужно беспокоиться об OpenGL или DirectX, вы просто реализуете интерфейс.

ВинСис

У него та же работа, что и у State Tracker, за исключением того, что он абстрагирует функциональность пространства ядра. Так что для Linux — например, он обрабатывает вещи, связанные с драйвером DRM для управления памятью и DRI для обработки.

Драйвер устройства

Вот где настоящий код. Здесь мы реализуем State Tracker API и WinSys API. Этот драйвер понимает только TGSI — промежуточный язык шейдеров. Затем он переводится в GLSL, а затем в набор инструкций графического процессора.

Итак, где же все это помещается в коде. Что ж, посетите репозиторий, если вы еще этого не сделали. Под src/gallium вы найдете весь код, который нам нужен для этого проекта. Точнее, gallium/drivers/freedreno здесь находится код драйвера для freedreno. Также есть gallium/include/pipe, он содержит важные заголовочные файлы для галлия.

PIPE_CONTEXT

Помните, мы говорили о том, что OpenGL — это конечный автомат? Ну, галлий применяет тот же контекст, в котором хранятся состояния, влияющие на то, как объекты отображаются в контексте. Эти структуры можно найти в gallium/include/pipe/p_context.h . Вы должны ознакомиться с тезисами. Они в основном контролируют отрисовку объектов и содержат различные другие функции настройки состояния.

PIPE_SCREEN

Затем есть экран, который в основном содержит все остальное, независимое от контекста. например информацию о названии устройства, производителе и возможностях (например, какие форматы текстур он поддерживает). ВЫ можете найти его здесь gallium/include/pipe/p_screen.h

PIPE_STATE

PIPE_STATE имеет структуры для абстрактных объектов состояния графического конвейера. Здесь вы найдете одну из самых важных структур, с которыми мы будем иметь дело в этом проекте: pipe_resource . Давайте посмотрим на его структуру:

  • width0: это ширина первого уровня MIP-карт.
  • height0: высота первого уровня.
  • depth0: глубина первого уровня.
  • format: это перечисление, в котором хранится формат пикселей текстуры. (Сколько битов занимает каждый канал и как адресуются пиксели, то есть нормализованы со знаком и т. д.). См. pipe_format .
  • target: Битовое поле, в котором хранится тип текстуры (2D, 3D, 2DArray и т. д.).
  • last_level: Индекс последнего уровня MIP-карты.
  • nr_samples: количество сэмплов, используемых в MSAA. Больше образцов означает лучшее качество.

Вы можете найти эту структуру в gallium/include/pipe/p_state.h . Важно помнить, что это общий параметр, и драйверы устройств могут переопределить его, чтобы позже добавить больше структур. Вскоре мы увидим, как Freedreno сделает это.
Вы можете подумать, а как насчет других уровней? Что ж, размеры уровней мипмапов являются производными от родительского уровня. Мы просто продолжаем делить на два, пока не достигнем размера в один пиксель.

Утилиты

В этом проекте мы также увидим использование многих служебных функций в коде. Я думаю, мы должны дать им вращение, чтобы понять, что они делают. Они расположены в gallium/auxiliary/util/ . Давайте представим две функции, которые будут часто использоваться. align() и u_minify() в u_math.h .

  • align(): Align делает то, что следует из его названия. Учитывая значение и выравнивание, функция обязательно вернет значение, кратное выравниванию. Точнее, наименьшее кратное, большее заданного значения.
  • u_minify(): Помните, мы говорили о получении размера уровней MIP-карты. Ну, это именно то, что делает эта функция. Он делит измерение на два и следит за тем, чтобы оно было не меньше 1.

Фридрено

Теперь мы переходим к настоящему MVP: галлиевый драйвер freedreno. Он реализует драйверы устройств графических процессоров Adreno. Базовый каталог содержит файлы, которые являются общими для каждого графического процессора в серии. Но не все графические процессоры созданы одинаковыми. Разные поколения имеют разные характеристики. В этом проекте мы будем иметь дело с 3-м поколением, а именно с Adreno 306. Исходные файлы, относящиеся к этому поколению, находятся в папке a3xx. Мы узнаем, что делает каждая единица перевода, пока работаем над проектом.

Теперь о самом важном в этом проекте: нам нужно включить тайлинг при отладке, чтобы иметь возможность его протестировать. В основном это состоит из двух функций, одна из которых возвращает тип макета текстуры в памяти, а другая устанавливает срезы, чтобы помочь с алгоритмом тайлинга. Эти две функции являются частью экрана, но на этот раз не экран канала, нет; экран фридрено. Итак, вы сможете найти их в freedreno_screen.h . Если вы перейдете к freedreno_screen.c, чтобы увидеть их реализацию, вы обнаружите, что каждое поколение может переопределять эти функции, чтобы обеспечить собственную реализацию. Вы можете увидеть, как настраиваются тезисы, посетив fd3_screen.c, вы увидите функцию fd3_screen_init(). Теперь все, что нам нужно сделать, это передать две функции, о которых мы говорили, в fd_screen.

screen->setup_slices = fd3_setup_slices;
if (fd_mesa_debug & FD_DBG_TTILE)
    screen->tile_mode = fd3_tile_mode;

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

Здорово! Но теперь, как мы определяем эти две функции.

Сначала давайте начнем с создания файла с именем fd3_resource.c, похожего на его братьев, и создадим соответствующий заголовочный файл. Прежде чем мы что-нибудь напишем, давайте не забудем добавить эти файлы в систему контроля версий. Здесь поддерживаются две системы сборки. Meson рекомендуемый способ сборки этого проекта и Makefile для способа, который требует меньше зависимостей сборки от Linux. Давайте откроем meson.build и добавим эти два файла. Это простой процесс, просто имитируйте синтаксис, который уже есть. И сделайте то же самое для Makefile.sources .

Теперь, что касается двух функций. Начнем с написания первого. Что ж, на самом деле все просто, все, что нам нужно сделать, это вернуть значение перечисления a3xx_tile_mode. Это перечисление определено в src/freedreno/registers/a3xx.xml.h . Это в основном указывает, какой тип макета мы используем в памяти. Либо плитка, либо линейка. На данный момент мы всегда будем возвращать TILE_32X32, то есть до тех пор, пока не найдем случай, когда тайлинг не будет работать или будет лучшим вариантом из двух. Вы можете просмотреть исходный код этой функции и все изменения, которые мы будем вносить здесь.

Что касается функции setup_slices(), мы, опять же, упростим ее, скопировав общую функцию setup_slices() из freedreno_resource.c, мы будем вносить изменения в эту функцию по мере продвижения. Теперь осталось только добавить эти функции на экран в fd3_screen_init(). Мы почти на месте.

Один из способов сделать это — просмотреть предыдущие коммиты, в которых добавлена ​​поддержка мозаичных текстур. Вы можете перейти на fd6_screen_init(), где уже есть поддержка тайлинга. И git blame строка, которая добавляет setup_slices() функцию, или tig blame та же строка для более интерактивного способа. Вы можете видеть в этом коммите, что тайлинг использовался в другом месте, а точнее в fd3_gmem.c .

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

Это завершение этой истории. Мы поговорим больше о setup_slices() и о том, как мы можем протестировать наши изменения в реальном мире, чтобы учесть различные требования к выравниванию.