Конвейер рендеринга 2D в Android поддерживает аппаратное ускорение, это означает, что все операции рисования на холсте выполняются с помощью графического процессора. Из-за увеличения ресурсов, необходимых для включения аппаратного ускорения, ваше приложение будет потреблять больше оперативной памяти.
Аппаратное ускорение включено по умолчанию. Если ваше приложение использует только стандартные компонуемые объекты, его глобальное включение не должно вызывать каких-либо негативных последствий для отрисовки. Однако, поскольку аппаратное ускорение поддерживается не для всех операций 2D-отрисовки, его включение может повлиять на некоторые из ваших пользовательских вызовов отрисовки. Проблемы обычно проявляются в виде невидимых элементов, исключений или неправильно отображаемых пикселей. Для решения этой проблемы Android предоставляет возможность включать или отключать аппаратное ускорение на нескольких уровнях. См. раздел «Управление аппаратным ускорением» .
Если ваше приложение выполняет пользовательскую отрисовку, протестируйте его на реальных аппаратных устройствах с включенным аппаратным ускорением, чтобы выявить любые проблемы. В разделе «Поддержка операций отрисовки» описаны известные проблемы с аппаратным ускорением и способы их решения.
См. также OpenGL с использованием API фреймворка .
Управление аппаратным ускорением
Вы можете управлять аппаратным ускорением на следующих уровнях:
- Приложение
- Активность
- Окно
- Композируемый
Уровень приложения
В файле манифеста Android добавьте следующий атрибут к тегу <application> , чтобы включить аппаратное ускорение для всего приложения:
<application android:hardwareAccelerated="true" ...>
Уровень активности
Если ваше приложение работает некорректно при глобально включенном аппаратном ускорении, вы можете управлять им и для отдельных активностей. Чтобы включить или отключить аппаратное ускорение на уровне активности, используйте атрибут android:hardwareAccelerated для элемента <activity> . В следующем примере аппаратное ускорение включено для всего приложения, но отключено для одной активности:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Уровень окна
Если вам требуется еще более точный контроль, вы можете включить аппаратное ускорение для заданного окна с помощью следующего кода:
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
Композируемый уровень
В Compose нет отдельного параметра для отключения аппаратного ускорения для каждого отдельного компонента.
Для отрисовки составного объекта на отдельном слое используйте Modifier.graphicsLayer . Это позволяет изменять свойства преобразования (такие как alpha , scaleX , scaleY , translationX , translationY , rotationX , rotationY , rotationZ и transformOrigin ) без повторного запуска кода отрисовки составного объекта. Для достижения наилучшей производительности всегда используйте лямбда-форму модификатора для установки этих свойств.
Чтобы явно задать буфер за пределами экрана для сложных операций рисования, таких как пользовательское смешивание внутри слоя, используйте CompositingStrategy.Offscreen . Дополнительную информацию см. в разделе «Графические модификаторы» .
Если у вас есть пользовательская операция отрисовки, которая требует исключительно программного рендеринга, вы можете использовать устаревший View с помощью AndroidView и вызвать setLayerType(View.LAYER_TYPE_SOFTWARE, null) для этого представления.
Поддержка операций черчения
При аппаратном ускорении конвейер 2D-рендеринга поддерживает наиболее часто используемые операции рисования Canvas , а также множество менее распространенных операций. Поддерживаются все операции рисования, используемые для рендеринга приложений, поставляемых с Android, стандартные компонуемые объекты и распространенные продвинутые визуальные эффекты, такие как отражения и тайловые текстуры.
В таблице ниже описан уровень поддержки различных операций на разных уровнях API:
| Первый поддерживаемый уровень API | ||||
| Холст | ||||
| drawBitmapMesh() (массив цветов) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() с вращением/перспективой | 18 | |||
| Краска | ||||
| setAntiAlias() (для текста) | 18 | |||
| setAntiAlias() (для строк) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (для линий) | 28 | |||
| setShadowLayer() (кроме текста) | 28 | |||
| setStrokeCap() (для линий) | 18 | |||
| setStrokeCap() (для точек) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (кадровый буфер) | 28 | |||
| PorterDuff.Mode.LIGHTEN (кадровый буфер) | 28 | |||
| PorterDuff.Mode.OVERLAY (framebuffer) | 28 | |||
| Шейдер | ||||
| ComposeShader внутри ComposeShader | 28 | |||
| Шейдеры одного типа внутри ComposeShader | 28 | |||
| Локальная матрица в ComposeShader | 18 | |||
Масштабирование холста
Аппаратное ускорение 2D-рендеринга изначально было разработано для поддержки отрисовки без масштабирования, при этом некоторые операции отрисовки значительно ухудшают качество при больших значениях масштаба. Эти операции реализованы в виде текстур, отрисованных в масштабе 1.0 и преобразованных графическим процессором. Начиная с API уровня 28, все операции отрисовки могут масштабироваться без проблем.
В следующей таблице показано, когда были внесены изменения в реализацию для корректной обработки больших масштабов:
| Операция чертежа масштабируется. | Первый поддерживаемый уровень API |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| Простые формы | 17 |
| Сложные формы | 28 |
| drawPath() | 28 |
| слой тени | 28 |
Если операция отрисовки, от которой вы зависите, не ускоряется аппаратно, отрисуйте соответствующий рисунок в программный Bitmap (или ImageBitmap ), находящийся за пределами экрана, и отобразите результат. Остальная часть пользовательского интерфейса будет использовать аппаратное ускорение.
Советы и рекомендации
Переход на аппаратное ускорение 2D-графики может мгновенно повысить производительность, но вам все равно следует проектировать приложение таким образом, чтобы оно эффективно использовало графический процессор, следуя этим рекомендациям:
- Сведите к минимуму сложность компоновки и необходимость перекомпоновки.
- Старайтесь, чтобы дерево компоновки было неглубоким, и ограничивайте количество перекомпоновок. Откладывайте чтение состояния до самой узкой области видимости, чтобы изменение приводило к перерисовке наименьшей возможной области. Например, считывайте анимированное состояние внутри
Modifier.graphicsLayer { }, а не в теле компонуемого объекта. Для получения дополнительной информации см. раздел «Производительность Jetpack Compose» . - Избегайте перерасхода средств.
- Не накладывайте слишком много слоев друг на друга. Удалите все элементы пользовательского интерфейса, которые полностью скрыты другими непрозрачными элементами, расположенными поверх них. Если вам нужно отобразить несколько слоев, смешанных друг с другом, рассмотрите возможность объединения их в один слой. Хорошее эмпирическое правило для современного оборудования — не отображать более чем в 2,5 раза больше пикселей, чем отображается на экране за кадр (прозрачные пиксели в растровом изображении тоже учитываются!).
- Не создавайте объекты рендеринга в методах отрисовки.
- Распространенная ошибка — создание нового объекта
Paintили нового объектаPathкаждый раз при вызове метода рендеринга. Это заставляет сборщик мусора запускаться чаще, а также обходит кэширование и оптимизации в аппаратном конвейере. Чтобы избежать этого, повторно используйте и изменяйте свои объекты:- Используйте стандартные методы : стандартные методы
DrawScope(например,drawRectиdrawCircle) уже повторно используют объектыPaintвнутри себя без необходимости выделения памяти разработчиком. - Изменяйте, а не перераспределяйте память : при написании собственной логики используйте
path.rewindдля очистки существующегоPath, а не для создания нового экземпляраPath. - Эффективное хранение состояния : внутри составного объекта выделяйте объекты один раз, используя
remember { Path() }. Если вы создаете многоразовые расширения пользовательских модификаторов, реализуйте пользовательскийModifier.Node, используяDrawModifierNode, чтобы выделять и повторно использовать объекты без создания новых выделений памяти в куче.
- Используйте стандартные методы : стандартные методы
- Не следует слишком часто изменять формы.
- Сложные фигуры, контуры и окружности, например, отрисовываются с помощью текстурных масок. Каждый раз, когда вы создаете или изменяете контур, аппаратный конвейер создает новую маску, что может быть затратным процессом.
- Не следует слишком часто изменять растровые изображения.
- При каждом изменении содержимого растрового изображения оно повторно загружается в виде текстуры на графический процессор при следующем отрисовке.
- Используйте альфа-канал с осторожностью.
- При создании полупрозрачного объекта с помощью API анимации
Modifier.alphaили Compose, он обычно отрисовывается во внеэкранном буфере, что удваивает необходимую скорость заполнения. Чтобы избежать накладных расходов на внеэкранный буфер для неперекрывающегося контента, установитеCompositingStrategy.ModulateAlpha. Для отдельных вызовов отрисовки применяйте альфа-канал непосредственно к команде отрисовки (например, с помощьюcolor = Color.Red.copy(alpha = 0.5f)) без создания слоя.