Accélération matérielle

Le pipeline de rendu Android 2D prend en charge l'accélération matérielle, ce qui signifie que toutes les opérations de dessin effectuées sur le canevas utilisent le GPU. En raison de l'augmentation des ressources requises pour activer l'accélération matérielle, votre application utilisera plus de RAM.

L'accélération matérielle est activée par défaut. Si votre application n'utilise que des composables standards, son activation globale ne devrait entraîner aucun effet négatif sur le dessin. Toutefois, comme l'accélération matérielle n'est pas compatible avec toutes les opérations de dessin en 2D, son activation peut affecter certains de vos appels de dessins personnalisés. Les problèmes se manifestent généralement par des éléments invisibles, des exceptions ou des pixels mal affichés. Pour remédier à ce problème, Android vous permet d'activer ou de désactiver l'accélération matérielle à plusieurs niveaux. Consultez Contrôler l'accélération matérielle.

Si votre application effectue un dessin personnalisé, testez-la sur des appareils matériels réels en activant l'accélération matérielle pour détecter les éventuels problèmes. La section Compatibilité avec les opérations de dessin décrit les problèmes connus liés à l'accélération matérielle et comment les contourner.

Consultez également OpenGL avec les API Framework.

Contrôler l'accélération matérielle

Vous pouvez contrôler l'accélération matérielle aux niveaux suivants :

  • Application
  • Activité
  • Fenêtre
  • Composable

Au niveau de l'application

Dans votre fichier manifeste Android, ajoutez l'attribut suivant à la balise <application> afin d'activer l'accélération matérielle sur l'ensemble de votre application :

<application android:hardwareAccelerated="true" ...>

Au niveau de l'activité

Si l'accélération matérielle est activée de façon globale et que votre application n'adopte pas le comportement attendu, vous pouvez également la contrôler pour chaque activité. Pour activer ou désactiver l'accélération matérielle au niveau de l'activité, vous pouvez utiliser l'attribut android:hardwareAccelerated de l'élément <activity>. L'exemple suivant active l'accélération matérielle sur l'ensemble de l'application, mais la désactive pour une activité :

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Au niveau de la fenêtre

Si vous avez besoin d'un contrôle encore plus précis, vous pouvez activer l'accélération matérielle pour une fenêtre donnée à l'aide du code suivant :

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

Niveau composable

Dans Compose, il n'existe pas de bouton par composable pour désactiver l'accélération matérielle.

Pour afficher un composable dans son propre calque, utilisez Modifier.graphicsLayer. Cela permet aux propriétés de transformation (telles que alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ et transformOrigin) de changer sans réexécuter le code de dessin du composable. Pour obtenir les meilleures performances, utilisez toujours la forme lambda du modificateur pour définir ces propriétés.

Pour forcer explicitement un tampon hors écran pour les opérations de dessin avancées, telles que le mélange personnalisé dans le calque, utilisez CompositingStrategy.Offscreen. Pour en savoir plus, consultez Modificateurs graphiques.

Si vous avez une opération de dessin personnalisée qui nécessite strictement le rendu logiciel, vous pouvez héberger une ancienne vue à l'aide de AndroidView et appeler setLayerType(View.LAYER_TYPE_SOFTWARE, null) sur cette vue.

Compatibilité avec les opérations de dessin

Lorsque le pipeline de rendu 2D est accéléré par le matériel, il accepte les opérations de dessin Canvas les plus courantes, ainsi que de nombreuses opérations moins utilisées. Toutes les opérations de dessin utilisées pour le rendu d'applications fournies avec Android, les composables standards et les effets visuels avancés courants tels que les reflets et les textures en mosaïque sont acceptés.

Le tableau suivant décrit le niveau de compatibilité des différentes opérations entre les niveaux d'API :

Premier niveau d'API compatible
Canevas
drawBitmapMesh() (éventail de couleurs) 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() avec rotation/perspective 18
Peindre
setAntiAlias() (pour du texte) 18
setAntiAlias() (pour des lignes) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (pour des lignes) 28
setShadowLayer() (autre que du texte) 28
setStrokeCap() (pour des lignes) 18
setStrokeCap() (pour des points) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN (framebuffer) 28
PorterDuff.Mode.LIGHTEN (framebuffer) 28
PorterDuff.Mode.OVERLAY (framebuffer) 28
Nuanceur
ComposeShader dans ComposeShader 28
Nuanceurs du même type dans ComposeShader 28
Matrice locale sur ComposeShader 18

Mise à l'échelle du canevas

Le pipeline de rendu 2D accéléré par le matériel a été conçu en premier pour pouvoir dessiner sans mise à l'échelle. Certaines opérations de dessin dégradent considérablement la qualité lorsque la mise à l'échelle se fait à des valeurs plus importantes. Ces opérations sont implémentées en tant que textures dessinées à l'échelle 1.0, transformées par le GPU. À partir du niveau d'API 28, toutes les opérations de dessin peuvent être mises à l'échelle sans problème.

Le tableau suivant indique à quel moment l'implémentation a été modifiée pour gérer correctement les opérations à grande échelle :

Opération de dessin devant être mises à l'échelle Premier niveau d'API compatible
drawText() 18
drawPosText() 28
drawTextOnPath() 28
Formes simples 17
Formes complexes 28
drawPath() 28
Couche d'ombre 28

Si une opération de dessin dont vous dépendez n'est pas accélérée par le matériel, affichez le dessin concerné dans un Bitmap (ou ImageBitmap) logiciel hors écran et dessinez le résultat. Le reste de votre UI conserve le chemin accéléré par le matériel.

Conseils et astuces

En passant à des graphismes 2D accélérés par le matériel, vous pouvez instantanément améliorer les performances. Toutefois, vous devez tout de même concevoir votre application de telle sorte qu'elle utilise efficacement le GPU en appliquant les recommandations suivantes :

Minimiser la complexité de la mise en page et de la recomposition
 Maintenez une faible profondeur de l'arborescence de mise en page et limitez le nombre de recompositions. Reportez les lectures d'état à la portée la plus étroite possible, afin qu'un changement redessine la plus petite région possible. Par exemple, lisez l'état animé à l'intérieur de Modifier.graphicsLayer { } plutôt que dans le corps d'un composable. Pour en savoir plus, consultez Performances de Jetpack Compose.
Éviter les superpositions
Ne superposez pas trop de calques. Supprimez tous les éléments d'interface utilisateur complètement obscurcis par d'autres éléments opaques. Si vous devez dessiner plusieurs couches fusionnées les unes aux autres, envisagez de les fusionner en une seule couche. En règle générale, avec le matériel actuel, il est recommandé de ne pas dessiner plus de 2,5 fois le nombre de pixels qui s'affichent à l'écran par frame (les pixels transparents d'un bitmap comptent).
Ne pas créer d'objets à afficher dans les méthodes de dessin
Une erreur courante consiste à créer un nouvel objet Paint ou Path chaque fois qu'une méthode d'affichage est appelée. Cela force le récupérateur de mémoire à s'exécuter plus souvent, et contourne également les caches et les optimisations dans le pipeline matériel. Pour éviter cela, réutilisez et modifiez vos objets :
  • Utilisez des méthodes standards : les méthodes DrawScope standards (comme drawRect et drawCircle) réutilisent déjà les objets Paint en interne sans nécessiter d'allocation par le développeur.
  • Effectuez une mutation au lieu de réallouer : lorsque vous écrivez une logique personnalisée, utilisez path.rewind pour effacer un Path existant au lieu d'en instancier un nouveauPath.
  • Conserver l'état de manière efficace : dans un composable, allouez des objets une seule fois à l'aide de remember { Path() }. Si vous créez des extensions de modificateur personnalisé réutilisables, implémentez un Modifier.Node personnalisé à l'aide de DrawModifierNode pour allouer et réutiliser les objets sans provoquer de nouvelles allocations de tas.
Ne pas modifier trop souvent les formes
Par exemple, les formes, les trajets et les cercles complexes sont affichés à l'aide de masques de texture. Chaque fois que vous créez ou modifiez un chemin d'accès, le pipeline matériel crée un masque qui peut s'avérer coûteux.
Ne pas modifier trop souvent les bitmaps
Chaque fois que vous modifiez le contenu d'un bitmap, celui-ci est réimporté en tant que texture GPU quand vous le dessinez la fois suivante.
Utiliser la version alpha avec précaution
Lorsque vous définissez un composable translucide à l'aide de Modifier.alpha ou des API d'animation Compose, il est généralement affiché dans un tampon hors écran, ce qui double le taux de remplissage requis. Pour éviter la surcharge du tampon hors écran pour le contenu non chevauchant, définissez CompositingStrategy.ModulateAlpha. Pour les appels de dessin individuels, appliquez l'alpha directement à la commande de dessin (comme avec color = Color.Red.copy(alpha = 0.5f)) sans créer de calque.

Ressources supplémentaires

Afficher le contenu