Este documento mostra como otimizar a performance do jogo usando ferramentas para identificar e resolver gargalos de CPU e GPU.
Otimização da CPU
Se a análise mostrar que o jogo está limitado pela CPU, é essencial investigar mais a fundo. Para isso, é necessário identificar as linhas de execução ou APIs específicas que causam gargalos e reduzem a taxa de FPS.
Para otimização da CPU, uma solução universal geralmente não é eficaz. Em vez disso, identifique a carga de trabalho mais exigente com base no jogo ou na cena e otimize a lógica e as funções relevantes.
Ferramentas de rastreamento de tempo do mecanismo de jogo
As ferramentas a seguir podem ajudar nessa análise:
Insights incríveis
Nos projetos do Unreal Engine, a ferramenta Unreal Insight facilita a análise das informações de rastreamento de tempo para threads individuais que compõem um frame.
Por exemplo, a GameThread geralmente usa a maior proporção de tempo da CPU, principalmente devido ao tempo de tick. Além disso, uma parte substancial do tempo de marcação é consumida por tarefas associadas a FActorComponentTickFunction.
Para otimizar FActorComponentTick, é fundamental excluir cálculos e
implementar a eliminação de caracteres e objetos posicionados fora do campo de visão da câmera. Além disso, usar animações baseadas no LOD (nível de detalhe) pode gerar mais melhorias de performance.
Unity Profiler (Unity)
A análise usando o Unity Profiler revela que a linha de execução principal consome mais de 45 ms, com PostLateUpdate.FinishFrameRendering ocupando 16,23 ms, o que a torna a operação mais demorada. Dentro disso, várias invocações de Inl_RenderCameraStack são observadas. É recomendável verificar a necessidade das câmeras ativadas e otimizá-las de acordo com isso.
Ferramentas de criação de perfis no nível do sistema
Use as seguintes ferramentas de criação de perfil:
Perfetto
Com o rastreamento do Perfetto, é possível determinar as atribuições de núcleo da CPU e os detalhes de execução de cada linha de execução em um dispositivo Android. Isso permite identificar gargalos de desempenho analisando dados de execução de linhas de execução.
Caso de sobrecarga da CPU
O rastreamento indica que a carga de trabalho nas GameThread e RenderThread está causando atrasos no QueuePresent da RHI Thread, levando a um cenário vinculado à CPU, com base no VSync.
Caso de sobrecarga da GPU
O rastreamento indica que a conclusão da GPU excede 25 ms, o que significa um cenário vinculado à GPU.
Simpleperf
Para identificar as funções com o maior uso atual de CPU, use o simpleperf. Para ter os melhores resultados, recomendamos classificar essas funções para priorizar e resolver primeiro aquelas com maior uso.
O Simpleperf ajuda a analisar dados sobre funções que usam mais tempo de CPU. Para otimizar o uso da CPU, comece com as funções que usam mais CPU. Neste exemplo, USkeletalMeshComponent, associado à animação em ActorComponentTickFunctions, usa mais CPU.
Otimização de GPU
Se a análise mostrar que o jogo é vinculado à GPU, será essencial fazer uma investigação mais detalhada. Isso exige o uso de várias ferramentas e técnicas para otimização e análise de GPU.
Para otimizar a GPU, use um depurador de frames para analisar o pipeline de renderização e as chamadas de desenho de cada cena. Além disso, é necessário entender bem a arquitetura da GPU e o comportamento do pipeline para identificar operações desnecessárias ou áreas a serem otimizadas.
As seções a seguir explicam métodos e ferramentas para otimização de GPU.
Eliminar RenderPasses desnecessários
Para melhorar o desempenho da renderização e reduzir a carga de trabalho da GPU, elimine as transmissões de renderização desnecessárias. Isso inclui qualquer transmissão de renderização que não tenha chamadas de desenho ou cuja saída não seja usada no frame final.
Use um depurador de GPU, como o RenderDoc, para analisar o pipeline de renderização e
identificar oportunidades de otimização.
Nenhuma chamada de desenho:verifique se a transmissão de renderização inclui alguma chamada de desenho. Se não houver chamadas de desenho, remova a transmissão.
Saída não usada:verifique se as transmissões subsequentes acessam ou mostram as saídas de transmissão de renderização, por exemplo, cor ou profundidade. Se não estiverem, remova o passe.
Cartões mescláveis:identifique os cartões que podem ser mesclados:
- Mesmo framebuffer ou anexos
- Operações de carga ou armazenamento compatíveis
- Sem barreiras de dependência entre eles
Minimizar operações de carga ou armazenamento
As operações de carga ou armazenamento consomem muitos recursos porque usam muita memória.
Minimize operações de carga e armazenamento desnecessárias. Realize essas ações apenas quando
anexos em um RenderPass forem necessários. Caso contrário, substitua-as por operações Clear ou Don't care para reduzir o overhead.
Como otimizar
Use um depurador de GPU, como o RenderDoc, para analisar o pipeline de renderização e
identificar as seguintes oportunidades de otimização:
Carregar:se um anexo de transmissão de renderização não usar dados de uma transmissão ou anexo anterior, uma operação de carregamento será desnecessária. Nesses casos, usar
Don't careouClearpode reduzir a sobrecarga.Armazenamento:se um anexo de transmissão de renderização não for usado após a transmissão atual, a operação de armazenamento será desnecessária. Nesses casos, use
Don't careouClear.Substituir:determine se as configurações atuais de carga ou armazenamento podem ser substituídas por
ClearouDon't Caresem afetar o frame final.
Evite o descarte para ativar o Early-Z
O Early-Z melhora o desempenho em plataformas móveis. No entanto, uma instrução discard
em um shader desativa automaticamente o Early-Z. Se a instrução discard não for essencial, remova-a.
Aceleração inicial no eixo Z
Essa otimização reduz significativamente as operações do sombreador de fragmentos e melhora o desempenho da GPU.
Early-Z Testes de profundidade e estêncil
Como otimizar
Use um depurador de GPU, como o RenderDoc, para analisar o pipeline de renderização e
identificar as seguintes oportunidades de otimização:
Uso de
discardem shaders de fragmentos:a palavra-chavediscardimpede que a GPU realize testes de profundidade antecipados porque a visibilidade do fragmento não é conhecida com antecedência.Modificação de
gl_FragDepth:a modificação dinâmica degl_FragDepthmuda a profundidade de um fragmento, o que desativa a otimização Early-Z porque a profundidade final é desconhecida antes do processamento do fragmento.Alfa para cobertura ativada:quando a opção alfa para cobertura está ativada (geralmente usada na renderização MSAA), a cobertura de fragmentos depende dos valores alfa. Isso pode atrasar o teste de profundidade e desativar o Early-Z.
Otimizar formato de textura
A seleção ideal do formato de textura reduz o consumo de memória, aumenta a eficiência da largura de banda e melhora o desempenho da renderização. Usar formatos de alta precisão pode desperdiçar recursos da GPU sem oferecer vantagens visuais.
Como otimizar
Use um depurador de GPU, como o RenderDoc, para analisar o pipeline de renderização e
identificar as seguintes oportunidades de otimização:
- Use
D24S8em vez deD32S8para buffers de profundidade-stencil:usarD24S8para buffers de profundidade-stencil reduz o consumo de memória em 20% em comparação comD32S8, com pouca ou nenhuma diferença perceptível na qualidade visual na maioria dos aplicativos. - Use a compactação
ASTCpara texturas de cores:a compactaçãoASTCreduz significativamente o uso da memória de textura, em até 8 vezes em comparação com formatos não compactados, preservando a alta qualidade visual. - Use formatos de ponto flutuante de meia precisão em vez de ponto flutuante de precisão total:use
R16FouRG16Fpara reduzir o consumo de largura de banda e armazenamento da memória. Esses formatos são adequados para buffers de pós-processamento.
Otimizar a complexidade da geometria
Minimizar a complexidade geométrica melhora o desempenho da renderização, principalmente em dispositivos móveis com recursos de GPU limitados. Isso envolve usar um número reduzido de vértices e triângulos, consolidar objetos para diminuir as chamadas de desenho e eliminar geometria não renderizada ou desnecessária. Técnicas como simplificação de malha, nível de detalhe (LOD) e eliminação de frustum ou oclusão podem reduzir significativamente a carga de trabalho da GPU e aumentar as taxas de frames.
Como otimizar
Use ferramentas de criação de perfil e depuradores de GPU, como RenderDoc, Android GPU
Inspector ou outros analisadores de performance, para identificar gargalos de
performance relacionados à geometria.
Reduzir a contagem de triângulos:minimize o uso de polígonos, principalmente para objetos pequenos ou distantes.
Use o nível de detalhamento (LOD): com base na distância da câmera, malhas mais simples são usadas automaticamente.
Mesclar malhas pequenas:consolide objetos estáticos para reduzir as chamadas de desenho e o overhead da CPU.
Frustum e remoção por oclusão:evite renderizar objetos que estão fora da visualização ou são obscurecidos por outros elementos.
Remover anexos desnecessários
Os anexos de transmissão de renderização (por exemplo, cor, profundidade, estêncil) consomem largura de banda de memória e recursos da GPU, mesmo que não sejam usados. Remover anexos desnecessários ou redundantes melhora o desempenho e reduz o consumo de energia, principalmente em plataformas móveis.
Como otimizar
Use ferramentas de criação de perfil e depuradores de GPU, como RenderDoc,
Android GPU Inspector ou outros analisadores de desempenho, para identificar
gargalos de desempenho relacionados à geometria.
- Verifique o uso real:há chamadas de desenho ou shaders que estão gravando ou lendo do anexo?
- Analisar a saída do frame:use
RenderDocou utilitários comparáveis para determinar se o anexo contribui para a imagem final. - Considere anexos temporários ou fictícios:anexos temporários ou uma operação de armazenamento "Não importa" devem ser usados para dados temporários que não exigem armazenamento permanente.
Otimizar a precisão do shader
Usar precisão excessivamente alta (por exemplo, highp em vez de mediump ou lowp) em shaders aumenta a carga de trabalho da GPU, o consumo de energia e a pressão de registro, principalmente em GPUs móveis. Ao usar a menor precisão adequada para variáveis (por exemplo, posições, cores, UVs), é possível melhorar o desempenho sem um impacto visual perceptível.
Como otimizar
Use ferramentas de criação de perfil e depuradores de GPU, como RenderDoc, Android GPU Inspector ou outros analisadores de desempenho, para identificar gargalos de desempenho relacionados à geometria.
Revise o código do shader:avalie as variáveis do shader e confirme se a alta precisão é usada apenas quando necessário, como para cálculos de profundidade ou espaço na tela. Use precisão média ou baixa para cores, coordenadas UV ou valores que não exigem alta precisão.
Use depuradores de GPU:utilitários de diagnóstico, como RenderDoc ou criadores de perfil de GPU móvel (por exemplo, AGI, Mali/GPU Inspector), identificam o uso elevado de registros ou interrupções de shader associadas a problemas de precisão.
Ativar a eliminação de faces traseiras
A renderização de triângulos que ficam de costas para a câmera (faces traseiras) geralmente é desnecessária para objetos sólidos.
Como otimizar
O uso de VK_CULL_MODE_NONE pode afetar negativamente o desempenho porque força
a GPU a renderizar as faces frontal e traseira, o que aumenta a carga de trabalho
de renderização.
Minimizar o overdraw em cenas da interface
Elimine chamadas de desenho e transmissões de renderização desnecessárias, principalmente em cenas de interface, para melhorar o desempenho da renderização e reduzir a carga de trabalho da GPU. Por exemplo, em uma cena de IU em que o mundo inteiro é renderizado antes de sobrepor a IU na tela, a renderização do mundo se torna redundante.
Como otimizar
Use um depurador de GPU, como o RenderDoc, para analisar o pipeline de renderização e
identificar as seguintes oportunidades de otimização:
- Verifique a ausência de overdraw supérfluo. Em contextos de interface do usuário, em que toda a tela pode ser renderizada, confirme se as transmissões de renderização anteriores não estão sendo desenhadas em excesso desnecessariamente.
- Ative o teste de profundidade e a eliminação para otimizar a performance.
- Considere a ordem de renderização da frente para trás.