Otimização do Vulkan do Godot Engine para Android

Imagem do mascote do Godot Engine

Visão geral

O Godot Engine é um mecanismo de jogo de código aberto multiplataforma conhecido com suporte robusto para Android. O Godot pode ser usado para criar jogos de praticamente qualquer gênero e é capaz de renderizar gráficos 2D e 3D. A versão 4 do Godot introduziu um novo sistema de renderização com recursos avançados para gráficos de alta fidelidade. O renderizador do Godot 4 foi projetado para APIs gráficas modernas, como a Vulkan.

A Godot Foundation contratou especialistas em otimização de gráficos da The Forge Interactive e colaborou com o Google para analisar e melhorar o renderizador Godot 4 Vulkan e mesclar essas otimizações de volta ao repositório do projeto. As otimizações ajudam os desenvolvedores a melhorar renderizadores Vulkan personalizados no Android.

Metodologia e resultados da otimização

O processo de otimização usou duas cenas 3D diferentes no Godot como destinos de comparação. O tempo de renderização das cenas foi medido em vários dispositivos durante cada iteração de otimização. Para se qualificar para inclusão, as mudanças no renderizador precisavam mostrar melhorias de desempenho em pelo menos alguns dispositivos testados e não podiam introduzir regressões de desempenho em nenhum dispositivo.

Várias arquiteturas de GPU do Android foram usadas nos testes. Embora muitas otimizações tenham trazido melhorias gerais, algumas tiveram um impacto maior em arquiteturas de GPU específicas. A soma total de todo o trabalho de otimização resultou em uma redução geral de 10% a 20% nos tempos de frame da GPU.

Otimização geral do Vulkan

O Forge realizou a refatoração de arquitetura geral no back-end de renderização do Godot Vulkan para melhorar o desempenho e ajudar o back-end a crescer com o aumento das demandas de renderização de conteúdo. As otimizações não são específicas para hardware móvel, mas beneficiam todas as plataformas Godot Vulkan.

Suporte a deslocamento dinâmico de UBO

Ao vincular um conjunto de descritores que contém um objeto de buffer uniforme dinâmico (UBO, na sigla em inglês), o Vulkan permite que os deslocamentos dinâmicos no UBO sejam especificados nos parâmetros de vinculação. Esse recurso pode ser usado para empacotar dados de várias operações de renderização em um único UBO, vinculando novamente o conjunto de descritores com um deslocamento dinâmico diferente para selecionar os dados adequados para o sombreador. O renderizador Godot Vulkan foi atualizado para poder usar deslocamentos dinâmicos em vez de sempre inicializar o deslocamento em zero. Essa melhoria permite otimizações de eficiência futuras.

Pools de conjuntos de descritores linear

Anteriormente, o comportamento padrão no renderizador Vulkan do Godot era criar todos os pools de conjuntos de descritores usando a flag VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, o que significa que as alocações de conjunto de descritores podiam ser liberadas de volta para o pool e realocações podiam ser feitas a partir do pool. Esse modo impôs uma sobrecarga adicional em comparação com os pools de conjuntos de descritores, que só permitem a alocação linear seguida por uma redefinição total do pool.

Quando possível, os pools de conjuntos de descritores agora são criados como lineares sem definir VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT. Os pools lineares são redefinidos como um todo quando necessário. Esse trabalho também inclui otimização adicional para vinculação de conjunto de descritores de lote quando possível, reduzindo o número de chamadas discretas para vkCmdBindDescriptorSets().

Suporte a amostragem imutável

Os objetos de amostragem que contêm dados de configuração de amostragem são vinculados tradicionalmente como parte dos dados do conjunto de descritores. Esse método permite que objetos de amostragem sejam trocados dinamicamente nos dados do conjunto de descritores. O Vulkan também oferece suporte a samplers imutáveis, que codificam os dados do sampler diretamente no layout do descritor. Essa configuração de amostragem é vinculada ao criar o conjunto de descritores e o estado do pipeline e não pode ser alterada após a criação.

Os samplers imutáveis trocam a flexibilidade por não precisar mais gerenciar e vincular objetos de sampler discretos. O renderizador Vulkan do Godot foi atualizado para oferecer suporte ao uso de amostras imutáveis. O uso de amostras foi alterado para usar amostras imutáveis quando prático.

Otimização para dispositivos móveis

Outras otimizações foram implementadas para melhorar especificamente o desempenho de renderização em hardware gráfico para dispositivos móveis. Em geral, as otimizações não são relevantes para hardwares gráficos de classe de desktop devido a diferentes designs de arquitetura.

As otimizações incluem:

  • Substituir o uso de constantes de push grandes
  • Alocação de buffer lenta
  • Suporte a buffer permanente
  • Mudança no modo de decodificação ASTC
  • Pré-rotação da tela

Substituir o uso de constantes de push grandes

As constantes de push são um recurso que permite injetar valores constantes para o programa de sombreador ativo no buffer de comando. As constantes de push são convenientes porque não exigem criação e preenchimento de buffer e não estão vinculadas a descritores. No entanto, as constantes de push têm um tamanho máximo limitado e podem afetar negativamente o desempenho em hardwares para dispositivos móveis.

Durante os testes em dispositivos Android, a performance foi aprimorada substituindo o uso constante de push de mais de 16 bytes por buffers uniformes. Os sombreadores que usavam 16 bytes ou menos de dados constantes tinham melhor desempenho com constantes de push. Além das considerações de desempenho, alguns hardwares gráficos têm alinhamento mínimo de 64 bytes para buffers uniformes, o que reduz a eficiência da memória devido ao preenchimento não utilizado em comparação com o uso de constantes de push.

Alocação de buffer lenta

A maioria dos hardwares gráficos para dispositivos móveis usa uma arquitetura de renderização diferida baseada em blocos (TBDR, na sigla em inglês). As GPUs que usam o TBDR dividem a região de tela maior em uma grade de blocos menores e renderizam cada bloco. Cada bloco é apoiado por uma pequena quantidade de RAM de alta velocidade que é usada para armazenamento pela GPU quando ela renderiza um bloco. Com o TBDR, os destinos de renderização que nunca são amostrados por outro destino fora da passagem de renderização podem permanecer totalmente na RAM do bloco e não exigem um buffer para uma loja de suporte de memória principal.

O VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT foi adicionado durante a criação de destinos de renderização adequados, como os destinos de cor e profundidade principais, para evitar a alocação de memória de buffer que nunca seria usada. A economia de memória de alocação preguiçosa em cenas de amostra foi medida como aproximadamente 50 megabytes de RAM.

Suporte a buffer permanente

O hardware móvel usa uma arquitetura de memória unificada (UMA) em vez de diferenciar o hardware entre RAM principal e RAM de gráficos. Quando a RAM principal e a RAM gráfica estão separadas, os dados precisam ser transferidos da RAM principal para a RAM gráfica para serem usados pela GPU. O Godot já implementa esse processo de transferência no renderizador Vulkan usando buffers de preparação. Em hardware UMA,um buffer de preparação é desnecessário para muitos tipos de dados. A memória pode ser usada pela CPU e pela GPU. O Forge implementou suporte a buffers compartilhados persistentes em hardwares compatíveis para eliminar o estágio quando possível.

Imagens de uma cena do Godot mostrando informações de perfil com e
        sem buffers persistentes ativados.
Figura 1. Criação de perfil de diferenças entre buffers persistentes ativados e desativados em uma cena de exemplo.

Mudança no modo de decodificação ASTC

A compactação de textura escalonável adaptável (ASTC) é o formato de compactação de textura moderno preferido em hardware de dispositivos móveis. Durante a descompressão, a GPU pode, por padrão, decodificar texels em um valor intermediário com uma precisão maior do que o necessário para fidelidade visual, resultando em uma perda de eficiência de texturização. Se o hardware de destino oferecer suporte, a extensão VK_EXT_astc_decode_mode será usada para especificar valores não normalizados de 8 bits por componente ao decodificar, em vez de valores de ponto flutuante de 16 bits.

Pré-rotação da tela

Para ter o desempenho ideal ao usar o Vulkan no Android, os jogos precisam reconciliar a orientação do dispositivo da tela com a orientação da superfície de renderização. Esse processo é chamado de pré-rotação. A falha na pré-rotação pode resultar em uma redução no desempenho devido ao SO Android precisar adicionar uma transmissão do compositor para girar manualmente as imagens. O suporte à pré-rotação no Android foi adicionado ao renderizador do Godot.

Melhorias na depuração

Além de otimizações de desempenho, o The Forge melhorou a experiência de depuração de problemas gráficos no renderizador do Godot com as seguintes adições:

  • Extensão de falha do dispositivo
  • Breadcrumbs
  • Marcadores de depuração

Extensão de falha do dispositivo

Quando a GPU encontra um problema durante as operações de renderização, o driver do Vulkan pode retornar um resultado VK_ERROR_DEVICE_LOST de uma chamada da API do Vulkan. Por padrão, nenhuma informação de contexto adicional é fornecida sobre o motivo pelo qual o driver retornou VK_ERROR_DEVICE_LOST. A extensão VK_EXT_device_fault oferece um mecanismo para o driver fornecer mais informações sobre a natureza da falha. O Godot adicionou suporte para ativar a extensão de falha do dispositivo (se disponível) e para informar as informações retornadas pelo driver.

Pode ser difícil depurar uma falha ou interrupção da GPU. Para ajudar a identificar qual conteúdo gráfico pode ter sido renderizado no momento de uma falha, o suporte a breadcrumb foi adicionado ao renderizador do Godot. Os Breadcrumbs são valores definidos pelo usuário que podem ser anexados ao conteúdo em listas de renderização no gráfico de renderização. Os dados do breadcrumb são gravados antes do início de uma nova passagem de renderização. Se ocorrer uma falha ou interrupção de execução, o valor atual do caminho de navegação poderá ser usado para determinar quais dados podem ter causado o problema.

Marcadores de depuração

Os marcadores de depuração, quando compatíveis com o driver, são usados para nomear recursos. Isso permite que strings legíveis pelo usuário sejam associadas a operações como renderizações e recursos como buffers e texturas ao usar uma ferramenta gráfica, como o RenderDoc. O suporte a anotações de marcadores de depuração foi adicionado ao renderizador Vulkan do Godot.

Blog do Godot Engine: atualização sobre a colaboração com o Google e o Forge

Pedido de pull de colaboração do Godot Engine Vulkan