Какова причина (и) задержки ввода/отображения в андроиде?

У меня очень простое приложение, которое отображает квадрат с opengl, входные данные считываются GLSurfaceView, а последняя позиция обменивается с потоком рендеринга с использованием изменчивой переменной.

То, что я наблюдаю (а также очень хорошо описано в https://www.mail-archive.com/[removed_email]/msg235325.html), заключается в том, что между положением касания есть задержка (отставание) и дисплей.

При активации опции разработчика, чтобы показать позицию касания, я вижу, что при быстром движении:

  • круговое отслеживание положения касания, показанного системой, задерживается на 1 см по сравнению с положением пальца. Я предполагаю, что это зависит от системы/оборудования и не может быть исправлено приложением.

  • но движение, сделанное в моем приложении, задерживается также на 1 см после круга отладки. Поэтому на практике задержка вдвое больше, чем может быть.

Когда движение пальца замедляется, конечная позиция чертежа останавливается и текущее положение касания синхронизируется. Именно так, как показано в этом видео (ссылка найдена на обсуждении выше) http://www.youtube.com/watch?v=fWZGshsXDhM

Тем не менее я не знаю, что вызывает вторую задержку, я приурочил обмен позиции от onTouchEvent к потоку рендеринга, и это всего лишь несколько мс. Рендеринг происходит с частотой 18 мс.

Я пытаюсь использовать скорость, чтобы укоротить появление задержки, предсказав положение 18 мс, но это не изменило восприятие задержки.

Кто-нибудь объясняет такую задержку? Это вызвано системными слоями, прежде чем получить доступ к нему в onTouchEvent? В этом случае я не увижу никакого способа исправить это.

Специального ответа на группу google не было. Также меня беспокоит то, что в потоке упоминается, что задержка исчезает с использованием canva вместо OpenGL. Что вызывает больше вопросов, на которые он отвечает.

2 ответа

Недавно я изучал то же самое. Здесь есть хорошее объяснение внутренних элементов графического пакета Android:

https://source.android.com/devices/graphics/architecture.html

По сути SurfaceViews на android представляют собой конец производителя очереди буферов. Потребителем этих буферов является SurfaceFlinger, который обрабатывает обновление отображаемых поверхностей на следующем vsync. Главное, что это очередь. Выполнение eglSwapBuffers фактически не блокируется, если очередь не заполнена. Это означает, что если ваш рендеринг прост, GLSurfaceView очень быстро создает первые несколько кадров, заполняя буферную очередь, а затем переходит в устойчивое состояние "рендеринга каждые 16 мс".

Вы можете увидеть количество заполненных буферов в очереди с помощью systrace. Вы заметите, что GLSurfaceView обычно запускает период vsync с 1 кадром в очереди, когда ваша функция рендеринга заканчивается, это доходит до 2, что означает, что вам нужно будет ждать 2 vsyncs перед ее отображением (фактически, 3, я думаю, потому что SurfaceFlinger добавляет другой для композиции, но то же самое для всех).

Холст рисуется по требованию, а не постоянно, и поскольку этот чертеж обычно происходит гораздо меньше, чем скорость обновления дисплея, BufferQueue для этого окна обычно пуст, поэтому новый контент потребляется немедленно SurfaceFlinger на следующем vsync.

Вы можете зарегистрироваться для обратных вызовов на vsync, чтобы вы никогда не отображали больше, чем частоту обновления экрана. Это позволяет избежать эффекта "заполнения очереди как можно быстрее" непрерывного рендеринга GLSurfaceView. Однако я не уверен, что гарантия не будет заполнена, так как возможно, что SurfaceFlinger пропустит бит, в результате чего ваша очередь будет расти в любом случае. У меня есть вопрос по этому поводу:

Минимизировать задержку Android GLSurfaceView

Я выполнил тест, где я визуализирую на vsync, но пропускаю несколько кадров каждые несколько сотен, чтобы гарантировать, что очередь опустошается должным образом. В этом случае объекты, созданные GL-объектами, перемещаются в блокировку с помощью перекрестия наложения "указательный след". "Отладочный круг" по-прежнему немного впереди - я думаю, что он реализован как простая перестановка слоя во время композиции SurfaceFlinger нарисованных поверхностей, которая добавила бы дополнительный кадр перед другим рендерингом.


Насколько я вижу, эта проблема не имеет ничего общего с лазером ввода или рисунком OpenGL. Я предполагаю, что у вас есть проблема синхронизации между вашими потоками.

События ввода выдаются через поток пользовательского интерфейса Android, а рисунок - в приложении OpenGL. Предполагая, что ваше тестовое приложение выполнено, как связанный с вами (оптимизированный) пример, вы устанавливаете переменную x и y в потоке пользовательского интерфейса и используете их в потоке чертежа без синхронизации. На многоядерном процессоре (как в современных телефонах, таких как S3), скорее всего, эти протечки работают на разных ядрах (поскольку поток OGL активен все время). Таким образом, каждое ядро имеет кэшированную версию ваших переменных x и y которые не гарантируются для немедленного обновления с другим ядром.

Эта проблема может быть решена путем создания переменных volatile. Из спецификации Java 7:

Язык программирования Java позволяет потокам получать доступ к общим переменным (§17.1). Как правило, для обеспечения постоянного и надежного обновления общих переменных поток должен гарантировать, что он имеет исключительное использование таких переменных, получая блокировку, которая, как правило, обеспечивает взаимное исключение для этих общих переменных.

Язык программирования Java предоставляет второй механизм, изменчивые поля, который более удобен, чем блокировка для некоторых целей.

Поле может быть объявлено изменчивым, и в этом случае модель памяти Java гарантирует, что все потоки будут видеть согласованное значение для переменной (§17.4).

Последний комментарий в связанной вами группе также предлагает это решение (это, вероятно, последний комментарий, потому что он работал). Он также комментирует проблему использования двух изменчивых переменных, которые всегда используются и обновляются вместе (иногда вы можете увидеть эффект первой обновленной переменной, но не второй для одного кадра). Лучшим решением является использование для этого ONE volatile variable или применение механизма блокировки, который также обеспечивает атомарность.

Я написал несколько OpenGL-приложений и никогда не сталкивался с этой проблемой "входного лага", всегда используя изменчивые переменные. Конечно, вы должны ожидать, что на дисплее будет один кадр за входом, потому что вы только обновляете входные данные один раз за кадр. Таким образом, ваш объект, находящийся за рамкой отладки, кажется совершенно нормальным.

licensed under cc by-sa 3.0 with attribution.