Aceleração de hardware

O pipeline de renderização 2D do Android tem suporte à aceleração de hardware. Isso significa que todas as operações de desenho realizadas na tela usam a GPU. Devido ao aumento dos recursos necessários para ativar a aceleração de hardware, o app vai consumir mais memória RAM.

A aceleração de hardware é ativada por padrão. Se o aplicativo usar apenas combináveis padrão, a ativação global não vai causar efeitos adversos para os desenhos. No entanto, como a aceleração de hardware não é compatível com todas as operações de desenho 2D, a ativação pode afetar algumas das suas chamadas de desenho personalizadas. Em geral, os problemas se manifestam como elementos invisíveis, exceções ou pixels renderizados de maneira incorreta. Para resolver isso, o Android oferece a opção de ativar ou desativar a aceleração de hardware em vários níveis. Consulte Controlar a aceleração de hardware.

Se o aplicativo faz desenhos personalizados, ele precisa ser testado em dispositivos de hardware reais com a aceleração de hardware ativada para que você possa encontrar possíveis problemas. A seção Suporte para operações de desenho descreve problemas conhecidos da aceleração de hardware e como solucionar todos eles.

Consulte também OpenGL com as APIs Framework.

Controlar a aceleração de hardware

Você pode controlar a aceleração de hardware nos seguintes níveis:

  • Aplicativo
  • Atividade
  • Janela
  • Elemento combinável

Nível do aplicativo

No arquivo de manifesto do Android, adicione o seguinte atributo à tag <application> para ativar a aceleração de hardware para todo o aplicativo:

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

Nível da atividade

Se o aplicativo não se comportar da forma correta com a aceleração de hardware ativada globalmente, ela também poderá ser controlada para atividades específicas. Para ativar ou desativar a aceleração de hardware no nível de atividade, use o atributo android:hardwareAccelerated para o elemento <activity>. O exemplo a seguir ativa a aceleração de hardware para todo o aplicativo, mas a desativa para uma atividade:

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

Nível da janela

Se você precisar de um controle ainda mais refinado, poderá ativar a aceleração de hardware para uma janela específica com o seguinte código:

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

Nível combinável

No Compose, não há uma chave por elemento combinável para desativar a aceleração de hardware.

Para renderizar um elemento combinável na própria camada, use Modifier.graphicsLayer. Isso permite que propriedades de transformação (como alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ e transformOrigin) mudem sem executar novamente o código de desenho do elemento combinável. Para ter o melhor desempenho, sempre use a forma lambda do modificador para definir essas propriedades.

Para forçar explicitamente um buffer fora da tela para operações de desenho avançadas, como fusão personalizada na camada, use CompositingStrategy.Offscreen. Para mais informações, consulte Modificadores gráficos.

Se você tiver uma operação de desenho personalizada que exija estritamente a renderização de software, hospede uma visualização legada usando AndroidView e chame setLayerType(View.LAYER_TYPE_SOFTWARE, null) nessa visualização.

Suporte a operações de desenho

Quando acelerado por hardware, o pipeline de renderização 2D é compatível com as operações de desenho Canvas mais usadas, assim como a várias operações menos comuns. Há suporte para todas as operações de desenho usadas para renderizar aplicativos que vêm com o Android, elementos combináveis padrão e efeitos visuais avançados comuns, como reflexos e texturas ladrilhadas.

A tabela a seguir descreve o nível de suporte de várias operações em diferentes níveis de API:

Primeiro nível da API com suporte
Canvas
drawBitmapMesh() (matriz de cores) 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() com rotação/perspectiva 18
Paint
setAntiAlias() (para texto) 18
setAntiAlias() (para linhas) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (para linhas) 28
setShadowLayer() (que não seja texto) 28
setStrokeCap() (para linhas) 18
setStrokeCap() (para pontos) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN (framebuffer) 28
PorterDuff.Mode.LIGHTEN (framebuffer) 28
PorterDuff.Mode.OVERLAY (framebuffer) 28
Shader
ComposeShader dentro do ComposeShader 28
Mesmo tipo de sombreadores dentro do ComposeShader 28
Matriz local no ComposeShader 18

Escalonamento da tela

O pipeline de renderização 2D acelerado por hardware foi criado para oferecer suporte a desenhos não dimensionados, com algumas operações de desenho degradando significativamente a qualidade em valores de escala mais altos. Essas operações são implementadas como texturas desenhadas na escala 1.0, transformadas pela GPU. A partir do nível 28 da API, todas as operações de desenho podem ser escalonadas sem problemas.

A tabela a seguir mostra quando a implementação foi alterada para processar grandes escalas corretamente:

Operação de desenho a ser escalonada Primeiro nível da API com suporte
drawText() 18
drawPosText() 28
drawTextOnPath() 28
Formas simples 17
Formas complexas 28
drawPath() 28
Camada de sombra 28

Se uma operação de desenho de que você depende não for acelerada por hardware, renderize o desenho afetado em um software Bitmap (ou ImageBitmap) fora da tela e desenhe o resultado. O restante da interface mantém o caminho acelerado por hardware.

Dicas e sugestões

A mudança para gráficos 2D acelerados por hardware pode aumentar a performance de forma instantânea. Mesmo assim, siga estas recomendações e projete seu aplicativo para que ele use a GPU de modo eficaz:

Minimizar a complexidade e a recomposição do layout
Mantenha a árvore de layout rasa e limite a quantidade de recomposições. Adie as leituras de estado para o escopo mais restrito possível, para que uma mudança redesenhe a menor região possível. Por exemplo, leia o estado animado dentro de Modifier.graphicsLayer { } em vez do corpo de um elemento combinável. Para mais informações, consulte Performance do Jetpack Compose.
Evite o overdraw
Não desenhe muitas camadas colocando umas em cima das outras. Remova todos os elementos da interface que estejam completamente encobertos por outros elementos opacos. Se você precisar desenhar várias camadas combinadas umas sobre as outras, mescle todas elas em uma única camada. Uma regra de ouro com o hardware atual é não desenhar mais que 2,5 vezes o número de pixels na tela por frame (pixels transparentes em uma contagem de bitmap).
Não crie objetos de renderização em métodos de desenho
Um erro comum é criar um novo objeto Paint ou um novo Path sempre que um método de renderização é invocado. Isso força o coletor de lixo a ser executado com mais frequência, além de ignorar os caches e as otimizações no pipeline de hardware. Para evitar isso, reutilize e faça mutações nos seus objetos:
  • Use métodos padrão: os métodos DrawScope padrão (como drawRect e drawCircle) já reutilizam objetos Paint internamente sem exigir alocação do desenvolvedor.
  • Faça mutações em vez de realocar: ao escrever uma lógica personalizada, use path.rewind para limpar um Path atual em vez de instanciar um novo Path.
  • Mantenha o estado de maneira eficiente: dentro de um elemento combinável, aloque objetos uma vez usando remember { Path() }. Se você estiver criando extensões de modificadores personalizados reutilizáveis, implemente um Modifier.Node personalizado usando DrawModifierNode para alocar e reutilizar os objetos sem causar novas alocações de heap.
Não modifique formas com muita frequência
Formas, caminhos e círculos complexos, por exemplo, são renderizados usando máscaras de textura. Toda vez que você cria ou modifica um caminho, o pipeline de hardware cria uma nova máscara, o que pode ser custoso.
Não modifique bitmaps com muita frequência
Sempre que você mudar o conteúdo de um bitmap, ele será enviado novamente como uma textura de GPU na próxima vez que for desenhado.
Use a Alfa com cuidado
Quando você torna um elemento combinável translúcido usando Modifier.alpha ou APIs de animação do Compose, ele geralmente é renderizado em um buffer fora da tela que dobra a taxa de preenchimento necessária. Para evitar a sobrecarga do buffer fora da tela em conteúdo não sobreposto, defina CompositingStrategy.ModulateAlpha. Para chamadas de desenho individuais, aplique o alfa diretamente ao comando de desenho (como com color = Color.Red.copy(alpha = 0.5f)) sem criar uma camada.

Outros recursos

Visualiza conteúdo