Diretrizes de design da Vulkan

A Vulkan é diferente das APIs gráficas anteriores, já que os drivers não realizam determinadas otimizações para apps com ela, como reutilização de canal. Em vez disso, os apps que usam Vulkan precisam implementar essas otimizações por conta própria. Se não fizerem isso, poderão apresentar desempenho pior do que apps que usam OpenGL ES.

Ao implementar essas otimizações, os apps têm potencial de se saírem muito melhor do que o driver, porque têm acesso a informações mais específicas para determinado caso de uso. Como resultado, otimizar com habilidade um app que usa Vulkan pode gerar melhor desempenho do que se o app estivesse usando OpenGL ES.

Esta página apresenta diversas otimizações que seu app Android pode implementar para ter melhorias de desempenho com a Vulkan.

Aceleração de hardware

A maioria dos dispositivos é compatível com a Vulkan 1.1 via aceleração de hardware, enquanto um pequeno subconjunto é compatível com a emulação de software. Os aplicativos podem detectar um dispositivo Vulkan baseado em software usando vkGetPhysicalDeviceProperties e verificando o campo deviceType da estrutura que é retornada. O SwiftShader (link em inglês) e outras implementações baseadas em CPU têm o valor VK_PHYSICAL_DEVICE_TYPE_CPU. Os aplicativos podem verificar especificamente o SwiftShader pela seleção dos campos vendorID e deviceID dessa mesma estrutura para valores específicos do SwiftShader.

Os aplicativos essenciais para o desempenho precisam evitar o uso de implementações da Vulkan emuladas por software e, em vez disso, recorrer ao OpenGL ES.

Aplicar rotação de tela durante a renderização

Quando a direção voltada para cima de um app não corresponde à orientação da tela do dispositivo, o compositor gira as imagens da cadeia de troca do app para criar essa correspondência. Ele realiza essa rotação à medida que exibe as imagens, o que resulta em mais consumo de energia (às vezes, muito mais) do que se não houvesse rotação.

Por outro lado, girar imagens da cadeia de troca durante a geração delas resulta em pouco ou nenhum consumo de energia extra. O campo VkSurfaceCapabilitiesKHR::currentTransform indica a rotação que o compositor aplica à janela. Depois que um app aplica essa rotação durante a renderização, ele usa o campo VkSwapchainCreateInfoKHR::preTransform para informar que a rotação foi concluída.

Minimizar passes de renderização por frame

Na maioria das arquiteturas de GPU de dispositivos móveis, começar e finalizar um passe de renderização é uma operação trabalhosa. Seu app pode melhorar o desempenho organizando operações de renderização no menor número possível de passes.

Diferentes operações de carregamento e armazenamento de anexo oferecem diferentes níveis de desempenho. Por exemplo, se você não precisa preservar o conteúdo de um anexo, pode usar VK_ATTACHMENT_LOAD_OP_CLEAR ou VK_ATTACHMENT_LOAD_OP_DONT_CARE, que são muito mais rápidos, em vez de VK_ATTACHMENT_LOAD_OP_LOAD. Da mesma forma, se você não precisa gravar os valores finais do anexo na memória para uso posterior, pode usar VK_ATTACHMENT_STORE_OP_DONT_CARE para conseguir um desempenho muito melhor que VK_ATTACHMENT_STORE_OP_STORE.

Além disso, na maioria dos passes de renderização, o app não precisa carregar nem armazenar o anexo de estêncil/profundidade. Nesses casos, é possível evitar a necessidade de alocar memória física para o anexo usando a sinalização VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT ao criar a imagem do anexo. Esse bit fornece os mesmos benefícios de glFramebufferDiscard no OpenGL ES.

Escolher tipos de memória adequados

Na alocação de memória do dispositivo, os apps podem escolher um tipo de memória. O tipo de memória determina como um app pode usar a memória e descreve as propriedades de captura e coerência da memória. Diferentes dispositivos têm tipos de memória diversos disponíveis, e diferentes tipos de memória apresentam características de desempenho diversificadas.

Um app pode usar um algoritmo simples para selecionar o melhor tipo de memória para determinado uso. Esse algoritmo seleciona o primeiro tipo de memória da matriz VkPhysicalDeviceMemoryProperties::memoryTypes que atende a dois critérios: o tipo de memória precisa ter permissão para o buffer ou imagem e precisa ter as propriedades mínimas que o app exige.

Os sistemas móveis geralmente não têm heaps de memória física separados para a CPU e a GPU. Nesses sistemas, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT não é tão importante quanto em sistemas que têm GPUs discretas com memória própria e dedicada. Nenhum app pode presumir que essa propriedade é obrigatória.

Agrupar conjuntos de descritor por frequência

Se você tem associações de recurso que mudam em diferentes frequências, use diversos conjuntos de descritor por canal em vez de reassociar todos os recursos para cada desenho. Por exemplo, você pode ter um conjunto de descritores para associações por cena, outro para associações por material e um terceiro para associações por instância de malha.

Use constantes imediatas para as mudanças de frequência mais alta, como as executadas em cada chamada de desenho.