Структура синхронизации

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

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

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

Явная синхронизация

Явная синхронизация позволяет производителям и потребителям графических буферов сигнализировать о завершении использования буфера. Явная синхронизация реализована в пространстве ядра.

Преимущества явной синхронизации включают в себя:

  • Меньше различий в поведении между устройствами
  • Улучшенная поддержка отладки
  • Улучшенные показатели тестирования

Платформа синхронизации имеет три типа объектов:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline — это монотонно увеличивающаяся временная шкала, которую поставщики должны реализовать для каждого экземпляра драйвера, например контекста GL, контроллера дисплея или 2D-блитера. sync_timeline подсчитывает задания, отправленные в ядро ​​для определенного оборудования. sync_timeline обеспечивает гарантии порядка операций и позволяет реализовать аппаратную реализацию.

Следуйте этим рекомендациям при реализации sync_timeline :

  • Укажите полезные имена для всех драйверов, временных шкал и границ, чтобы упростить отладку.
  • Реализуйте операторы timeline_value_str и pt_value_str на временных шкалах, чтобы сделать выходные данные отладки более читабельными.
  • Реализуйте fill driver_data , чтобы предоставить библиотекам пользовательского пространства, таким как библиотека GL, доступ к личным данным временной шкалы, если это необходимо. data_driver позволяет поставщикам передавать информацию о неизменяемых sync_fence и sync_pts для построения командных строк на их основе.
  • Не разрешайте пользовательскому пространству явно создавать или сигнализировать о ограждении. Явное создание сигналов/ограждений приводит к атаке типа «отказ в обслуживании», которая останавливает функциональность конвейера.
  • Не обращайтесь к элементам sync_timeline , sync_pt или sync_fence явно. API предоставляет все необходимые функции.

sync_pt

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

sync_fence

sync_fence — это набор значений sync_pt , которые часто имеют разные родительские элементы sync_timeline (например, для контроллера дисплея и графического процессора). sync_fence , sync_pt и sync_timeline — это основные примитивы, которые драйверы и пользовательское пространство используют для передачи своих зависимостей. Когда поступает сигнал об ограничении, все команды, выданные до ограничения, гарантированно будут завершены, поскольку драйвер ядра или аппаратный блок выполняет команды по порядку.

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

Ограждения, как и значения sync_pt , становятся активными и меняют состояние в зависимости от состояния своих точек. Если все значения sync_pt становятся сигнализированными, sync_fence становится сигнализированным. Если один sync_pt попадает в состояние ошибки, весь sync_fence находится в состоянии ошибки.

Членство в sync_fence является неизменяемым после создания ограждения. Чтобы получить более одной точки в заборе, выполняется слияние, при котором точки из двух разных заборов добавляются к третьему забору. Если одна из этих точек была сигнализирована в исходном ограждении, а другая — нет, то третье ограждение также не будет находиться в сигнальном состоянии.

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

  • Подсистема пространства ядра, реализующая структуру синхронизации для конкретного драйвера оборудования. Драйверы, которые должны поддерживать функцию ограждения, обычно представляют собой все, что имеет доступ к Hardware Composer или взаимодействует с ним. Ключевые файлы включают в себя:
    • Основная реализация:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • Документация по адресу kernel/common/Documentation/sync.txt
    • Библиотека для связи с пространством ядра в platform/system/core/libsync
  • Поставщик должен предоставить соответствующие границы синхронизации в качестве параметров функциям validateDisplay() и presentDisplay() в HAL.
  • Два расширения GL, связанные с ограничением ( EGL_ANDROID_native_fence_sync и EGL_ANDROID_wait_sync ) и поддержка ограничения в графическом драйвере.

Практический пример: реализация драйвера дисплея

Чтобы использовать API, поддерживающий функцию синхронизации, разработайте драйвер дисплея с функцией буфера дисплея. До появления платформы синхронизации эта функция получала объекты dma-buf , выводила эти буферы на дисплей и блокировала работу, пока буфер был виден. Например:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

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

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

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

Интеграция синхронизации

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

Соглашения об интеграции

Следуйте соглашениям интерфейса Android HAL:

  • Если API предоставляет дескриптор файла, который ссылается на sync_pt , драйвер поставщика или HAL, использующий API, должен закрыть дескриптор файла.
  • Если драйвер поставщика или HAL передает дескриптор файла, содержащий sync_pt , функции API, драйвер поставщика или HAL не должен закрывать дескриптор файла.
  • Чтобы продолжить использование дескриптора файла ограничения, драйвер поставщика или HAL должен дублировать дескриптор.

Объект ограждения переименовывается каждый раз, когда он проходит через BufferQueue. Поддержка ограждения ядра позволяет ограждениям иметь строки для имен, поэтому платформа синхронизации использует имя окна и индекс буфера, которые ставятся в очередь для присвоения имени ограждению, например SurfaceView:0 . Это полезно при отладке, чтобы определить источник взаимоблокировки, поскольку имена появляются в выводе /d/sync и отчетах об ошибках.

Интеграция с ANativeWindow

ANativeWindow учитывает ограничения. dequeueBuffer , queueBuffer и cancelBuffer имеют параметры ограничения.

Интеграция OpenGL ES

Интеграция синхронизации OpenGL ES опирается на два расширения EGL:

  • EGL_ANDROID_native_fence_sync предоставляет способ обернуть или создать собственные дескрипторы файлов ограждения Android в объектах EGLSyncKHR .
  • EGL_ANDROID_wait_sync допускает зависания на стороне графического процессора, а не на стороне процессора, заставляя графический процессор ждать EGLSyncKHR . Расширение EGL_ANDROID_wait_sync совпадает с расширением EGL_KHR_wait_sync .

Чтобы использовать эти расширения независимо, реализуйте расширение EGL_ANDROID_native_fence_sync вместе с соответствующей поддержкой ядра. Затем включите расширение EGL_ANDROID_wait_sync в вашем драйвере. Расширение EGL_ANDROID_native_fence_sync состоит из отдельного типа объекта собственного ограждения EGLSyncKHR . В результате расширения, которые применяются к существующим типам объектов EGLSyncKHR , не обязательно применяются к объектам EGL_ANDROID_native_fence , что позволяет избежать нежелательных взаимодействий.

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

  • Действительный файловый дескриптор ограждения оборачивает существующий собственный файловый дескриптор ограждения Android в объект EGLSyncKHR .
  • -1 создает собственный файловый дескриптор ограждения Android из объекта EGLSyncKHR .

Используйте вызов функции DupNativeFenceFD() , чтобы извлечь объект EGLSyncKHR из собственного файлового дескриптора ограждения Android. Это дает тот же результат, что и запрос атрибута set, но соответствует соглашению, согласно которому получатель закрывает ограждение (отсюда и дублирующая операция). Наконец, уничтожение объекта EGLSyncKHR закрывает атрибут внутреннего ограждения.

Интеграция с аппаратным композитором

Hardware Composer обрабатывает три типа ограничений синхронизации:

  • Ограждения сбора передаются вместе с входными буферами в вызовы setLayerBuffer и setClientTarget . Они представляют собой ожидающую запись в буфер и должны сигнализировать до того, как SurfaceFlinger или HWC попытается прочитать из связанного буфера для выполнения композиции.
  • Ограждения выпуска извлекаются после вызова presentDisplay с помощью вызова getReleaseFences . Они представляют собой ожидающее чтение из предыдущего буфера на том же уровне. Ограничитель освобождения сигнализирует, когда HWC больше не использует предыдущий буфер, поскольку текущий буфер заменил предыдущий буфер на дисплее. Ограничения выпуска передаются обратно в приложение вместе с предыдущими буферами, которые будут заменены во время текущей композиции. Приложение должно дождаться сигнала ограничения освобождения, прежде чем записывать новое содержимое в буфер, который был ему возвращен.
  • Текущие границы возвращаются по одному на кадр как часть вызова presentDisplay . Присутствующие границы обозначают завершение композиции этого кадра или, альтернативно, когда результат композиции предыдущего кадра больше не нужен. Для физических дисплеев presentDisplay возвращает существующие границы, когда текущий кадр появляется на экране. После возврата существующих границ можно безопасно снова выполнить запись в целевой буфер SurfaceFlinger, если это применимо. Для виртуальных дисплеев текущие границы возвращаются, когда чтение из выходного буфера безопасно.