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
Paintou um novoPathsempre 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
DrawScopepadrão (comodrawRectedrawCircle) já reutilizam objetosPaintinternamente sem exigir alocação do desenvolvedor. - Faça mutações em vez de realocar: ao escrever uma lógica personalizada, use
path.rewindpara limpar umPathatual em vez de instanciar um novoPath. - 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 umModifier.Nodepersonalizado usandoDrawModifierNodepara alocar e reutilizar os objetos sem causar novas alocações de heap.
- Use métodos padrão: os métodos
- 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.alphaou 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, definaCompositingStrategy.ModulateAlpha. Para chamadas de desenho individuais, aplique o alfa diretamente ao comando de desenho (como comcolor = Color.Red.copy(alpha = 0.5f)) sem criar uma camada.