Платформа синхронизации явно описывает зависимости между различными асинхронными операциями в графической системе 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, если это применимо. Для виртуальных дисплеев текущие границы возвращаются, когда чтение из выходного буфера безопасно.