Почему SurfaceFlinger все еще использует 5 мс процессорного времени на кадр с Hardware Composer?
Я пытаюсь запустить сложное и чувствительное к задержкам приложение со скоростью 60 кадров в секунду на как можно большем количестве устройств Android. Он включает в себя обработку живых кадров с камеры (в идеале также со скоростью 60 кадров в секунду) наряду с рендерингом дополнительной графики поверх OpenGL ES 2/3.
Во-первых, я просто пытаюсь определить и минимизировать любые накладные расходы на уровне системы, используя systrace с минимальным тестовым приложением, которое получает кадры камеры в SurfaceTexture и отображает их с помощью OpenGL ES 2 в GLSurfaceView.
Я исследовал Samsung Galaxy S8 (версия Exynos с графическим процессором Mali и 4-мя маленькими настройками процессора) с Android 8.0.
При получении кадров камеры, но не при их рендеринге (например, путем переключения GLSurfaceView на RENDERMODE_WHEN_DIRTY, а не на RENDERMODE_CONTINUOUSLY), загрузка ЦП оказывается довольно низкой по всей плате с небольшим объемом использования ЦП на кадр, который выглядит связанным с очередями и выгрузкой буферов для SurfaceTexture. SurfaceFlinger, кажется, ничего не делает, когда ни одна из поверхностей не обновляется, как ожидалось.
Как только я начинаю рендерить новые кадры, все становится интереснее. GLThread в моем приложении занимает всего ~1,5 мс процессорного времени, примерно то, что я ожидал. Что является неожиданным, так это время процессора, требуемое в SurfaceFlinger.
Вот немного вывода systrace, типичного для большинства кадров:
Каждый представленный кадр проходит 2 операции SurfaceFlinger - есть handleMessageInvalidate
это вызывает updateTexImage
и затем handleMessageRefresh
что в основном тратится на doComposition
, причем большая часть этого потрачена на postFramebuffer
,
Обратите внимание, что большую часть этого времени поток активен на процессоре, а не спит. Это примерно треть времени кадра для одного ядра, потраченного в SurfaceFlinger - это очень важно, если планировщик решит использовать то же ядро для одного из моих важных потоков.
Я прочитал довольно много внутренних документов, посвященных внутренним компонентам SurfaceFlinger, включая эту страницу, посвященную Композитору аппаратного обеспечения: https://source.android.com/devices/graphics/arch-sf-hwc.
Мое понимание HWC заключалось в том, что вся композиция была выполнена на оборудовании дисплея - я ожидал, что работа на стороне процессора будет минимальной; просто фиксируя последние буферы и передавая их в HWC.
dumpsys SurfaceFlinger
действительно показывает, что HWC используется для всех слоев:
| type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name
|-----------+------------+------+------+----+------+-------------+--------------------------------+------------------------+------
| HWC | 75cee57f40 | 0000 | 0020 | 00 | 0100 | RGBx_8888 | 0.0, 0.0, 1152.0, 2960.0 | 0, 0, 1152, 2960 | SurfaceView - com.example.tangobravo.camera1test/com.example.tangobravo.camera1test.MainActivity@e16091d@3#0
| HWC | 75cee59b40 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 1104.0, 0.0, 1440.0, 2960.0 | 1104, 0, 1440, 2960 | com.example.tangobravo.camera1test/com.example.tangobravo.camera1test.MainActivity#0
| HWC | 75cee58100 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 96.0, 2960.0 | 1344, 0, 1440, 2960 | StatusBar#0
| FB TARGET | 75cee55b60 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0.0, 0.0, 1440.0, 2960.0 | 0, 0, 1440, 2960 | HWC_FRAMEBUFFER_TARGET
Так, что происходит? Почему HWC так дорого здесь? Есть ли лучший (более низкие издержки) шаблон, который я должен использовать для приложения?
Я ожидаю, что композитор NEON CPU сможет пролистывать эти слои примерно за 5 мс, поэтому не похоже, что HWC обеспечивает большую выгоду с точки зрения использования процессора.