Accelerazione hardware

La pipeline di rendering 2D di Android supporta l'accelerazione hardware, il che significa che tutte le operazioni di disegno eseguite sul canvas utilizzano la GPU. A causa dell'aumento delle risorse necessarie per attivare l'accelerazione hardware, la tua app consumerà più RAM.

L'accelerazione hardware è attivata per impostazione predefinita. Se la tua applicazione utilizza solo composable standard, l'attivazione globale non dovrebbe causare effetti di disegno negativi. Tuttavia, poiché l'accelerazione hardware non è supportata per tutte le operazioni di disegno 2D, l'attivazione potrebbe influire su alcune chiamate di disegno personalizzate. I problemi si manifestano in genere come elementi invisibili, eccezioni o pixel visualizzati in modo errato. Per risolvere questo problema, Android ti offre la possibilità di attivare o disattivare l'accelerazione hardware a più livelli. Vedi Controllare l'accelerazione hardware.

Se la tua applicazione esegue disegni personalizzati, testala su dispositivi hardware reali con l'accelerazione hardware attivata per rilevare eventuali problemi. La sezione Supporto delle operazioni di disegno descrive i problemi noti relativi all'accelerazione hardware e come risolverli.

Consulta anche OpenGL con le API Framework.

Controllare l'accelerazione hardware

Puoi controllare l'accelerazione hardware ai seguenti livelli:

  • Applicazione
  • Attività
  • Finestra
  • Componibile

Livello di applicazione

Nel file manifest Android, aggiungi il seguente attributo al tag <application> per attivare l'accelerazione hardware per l'intera applicazione:

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

Livello di attività

Se la tua applicazione non si comporta correttamente con l'accelerazione hardware attivata a livello globale, puoi controllarla anche per le singole attività. Per attivare o disattivare l'accelerazione hardware a livello di attività, puoi utilizzare l'attributo android:hardwareAccelerated per l'elemento <activity>. L'esempio seguente abilita l'accelerazione hardware per l'intera applicazione, ma la disabilita per un'attività:

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

Livello della finestra

Se hai bisogno di un controllo ancora più granulare, puoi attivare l'accelerazione hardware per una determinata finestra con il seguente codice:

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

Livello componibile

In Compose non esiste un'opzione per composizione per disattivare l'accelerazione hardware.

Per eseguire il rendering di un componibile nel proprio livello, utilizza Modifier.graphicsLayer. In questo modo, le proprietà di trasformazione (come alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ e transformOrigin) cambiano senza eseguire nuovamente il codice di disegno del componibile. Per ottenere le prestazioni migliori, utilizza sempre la forma lambda del modificatore per impostare queste proprietà.

Per forzare esplicitamente un buffer off-screen per operazioni di disegno avanzate, ad esempio la fusione personalizzata all'interno del livello, utilizza CompositingStrategy.Offscreen. Per ulteriori informazioni, vedi Modificatori grafici.

Se hai un'operazione di disegno personalizzata che richiede rigorosamente il rendering software, puoi ospitare una visualizzazione precedente utilizzando AndroidView e chiamare setLayerType(View.LAYER_TYPE_SOFTWARE, null) su quella visualizzazione.

Supporto per le operazioni di disegno

Quando viene accelerata dall'hardware, la pipeline di rendering 2D supporta le operazioni di disegno Canvas più comunemente utilizzate, nonché molte operazioni meno utilizzate. Sono supportate tutte le operazioni di disegno utilizzate per il rendering delle applicazioni fornite con Android, dei composable standard e degli effetti visivi avanzati comuni, come riflessi e texture affiancate.

La tabella seguente descrive il livello di supporto di varie operazioni nei diversi livelli API:

Primo livello API supportato
Canvas
drawBitmapMesh() (array di colori) 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() con rotazione/prospettiva 18
Pittura
setAntiAlias() (per il testo) 18
setAntiAlias() (per le linee) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (per le linee) 28
setShadowLayer() (diverso dal testo) 28
setStrokeCap() (per le linee) 18
setStrokeCap() (per i punti) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN (framebuffer) 28
PorterDuff.Mode.LIGHTEN (framebuffer) 28
PorterDuff.Mode.OVERLAY (framebuffer) 28
Shader
ComposeShader all'interno di ComposeShader 28
Shader dello stesso tipo all'interno di ComposeShader 28
Matrice locale su ComposeShader 18

Scalabilità del canvas

La pipeline di rendering 2D con accelerazione hardware è stata creata per supportare il disegno non scalato, con alcune operazioni di disegno che peggiorano notevolmente la qualità a valori di scala più elevati. Queste operazioni vengono implementate come texture disegnate in scala 1.0, trasformate dalla GPU. A partire dal livello API 28, tutte le operazioni di disegno possono essere scalate senza problemi.

La tabella seguente mostra quando l'implementazione è stata modificata per gestire correttamente le grandi scale:

Operazione di disegno da scalare Primo livello API supportato
drawText() 18
drawPosText() 28
drawTextOnPath() 28
Forme semplici 17
Forme complesse 28
drawPath() 28
Livello di ombreggiatura 28

Se un'operazione di disegno da cui dipendi non è accelerata dall'hardware, esegui il rendering del disegno interessato in un Bitmap (o ImageBitmap) software off-screen e disegna il risultato. Il resto dell'interfaccia utente mantiene il percorso con accelerazione hardware.

Suggerimenti utili

Il passaggio alla grafica 2D con accelerazione hardware può aumentare immediatamente le prestazioni, ma devi comunque progettare l'applicazione in modo che utilizzi la GPU in modo efficace seguendo questi consigli:

Ridurre al minimo la complessità e la ricomposizione del layout
Mantieni l'albero del layout poco profondo e limita il numero di ricomposizioni. Rimanda le letture dello stato all'ambito più ristretto, in modo che una modifica ridisegni la regione più piccola possibile. Ad esempio, leggi lo stato animato all'interno di Modifier.graphicsLayer { } anziché nel corpo di un elemento componibile. Per saperne di più, consulta Prestazioni di Jetpack Compose.
Evitare lo scoperto di conto
Non disegnare troppi livelli uno sopra l'altro. Rimuovi gli elementi dell'interfaccia utente completamente oscurati da altri elementi opachi sopra di essi. Se devi disegnare più livelli miscelati uno sopra l'altro, valuta la possibilità di unirli in un unico livello. Una buona regola generale con l'hardware attuale è di non disegnare più di 2,5 volte il numero di pixel sullo schermo per fotogramma (vengono conteggiati anche i pixel trasparenti in una bitmap).
Non creare oggetti di rendering nei metodi di disegno
Un errore comune è creare un nuovo Paint o un nuovo Path ogni volta che viene richiamato un metodo di rendering. In questo modo, il garbage collector viene eseguito più spesso e vengono ignorate anche le cache e le ottimizzazioni nella pipeline hardware. Per evitare questo problema, riutilizza e modifica gli oggetti:
  • Utilizza metodi standard: i metodi standard DrawScope (come drawRect e drawCircle) riutilizzano già internamente gli oggetti Paint senza richiedere l'allocazione da parte dello sviluppatore.
  • Modifica anziché riassegnazione: quando scrivi una logica personalizzata, utilizza path.rewind per cancellare un Path esistente anziché crearne uno nuovo Path.
  • Gestisci in modo efficiente lo stato di attesa: all'interno di un componibile, alloca gli oggetti una sola volta utilizzando remember { Path() }. Se crei estensioni dei modificatori personalizzati riutilizzabili, implementa un Modifier.Node personalizzato utilizzando DrawModifierNode per allocare e riutilizzare gli oggetti senza causare nuove allocazioni dell'heap.
Non modificare le forme troppo spesso
Forme, tracciati e cerchi complessi, ad esempio, vengono visualizzati utilizzando maschere di texture. Ogni volta che crei o modifichi un percorso, la pipeline hardware crea una nuova maschera, che può essere costosa.
Non modificare troppo spesso le bitmap
Ogni volta che modifichi il contenuto di una bitmap, questa viene caricata di nuovo come texture GPU la volta successiva che la disegni.
Utilizzare la versione alpha con attenzione
Quando crei un elemento traslucido componibile utilizzando Modifier.alpha o le API di animazione Compose, in genere viene eseguito il rendering in un buffer off-screen, il che raddoppia la velocità di riempimento richiesta. Per evitare l'overhead del buffer fuori schermo per i contenuti non sovrapposti, imposta CompositingStrategy.ModulateAlpha. Per le singole chiamate di disegno, applica l'alpha direttamente al comando di disegno (come con color = Color.Red.copy(alpha = 0.5f)) senza creare un livello.

Risorse aggiuntive

Visualizza contenuti