Consignes de conception avec Vulkan

Vulkan est différent des API graphiques plus anciennes en ce sens que les pilotes n'effectuent pas certaines optimisations, telles que la réutilisation du pipeline, pour les applications. Au lieu de cela, les applications qui utilisent Vulkan doivent implémenter ces optimisations elles-mêmes. Si elles ne le font pas, elles peuvent offrir de moins bonnes performances que les applications utilisant OpenGL ES.

Lorsque les applications implémentent elles-mêmes ces optimisations, elles peuvent le faire plus efficacement que le pilote, car elles ont accès à des informations plus précises pour un cas d'utilisation donné. Par conséquent, une application savamment optimisée qui utilise Vulkan peut générer de meilleures performances que si elle utilisait OpenGL ES.

Cette page présente plusieurs optimisations que votre application Android peut mettre en œuvre pour être plus performante grâce à Vulkan.

Accélération matérielle

La plupart des appareils sont compatibles avec Vulkan 1.1 via l'accélération matérielle, tandis qu'un petit sous-ensemble d'entre eux le sont via l'émulation logicielle. Les applications peuvent détecter un appareil Vulkan à émulation logicielle à l'aide de vkGetPhysicalDeviceProperties, en vérifiant le champ deviceType de la structure renvoyée. SwiftShader et les autres intégrations basées sur le processeur ont la valeur VK_PHYSICAL_DEVICE_TYPE_CPU. Les applications peuvent détecter spécifiquement SwiftShader en recherchant les valeurs correspondantes dans les champs vendorID et deviceID de cette même structure.

Les applications pour lesquelles les performances sont un aspect critique doivent éviter d'utiliser des implémentations Vulkan émulées par logiciel et utiliser OpenGL ES à la place.

Appliquer la rotation de l'écran pendant le rendu

Lorsque la direction vers le haut d'une application ne correspond pas à l'orientation de l'écran de l'appareil, le compositeur fait pivoter les images de la chaîne d'échange de l'application pour qu'elles correspondent. Il effectue cette rotation lors de l'affichage des images, ce qui entraîne une consommation d'énergie plus importante (parfois beaucoup plus) que s'il ne les faisait pas pivoter.

En revanche, la rotation des images de la chaîne d'échange pendant leur génération entraîne une consommation d'énergie supplémentaire minime, voire nulle. Le champ VkSurfaceCapabilitiesKHR::currentTransform indique la rotation que le compositeur applique à la fenêtre. Une fois que l'application a effectué cette rotation lors du rendu, elle utilise le champ VkSwapchainCreateInfoKHR::preTransform pour signaler que la rotation est terminée.

Limiter le nombre de passes de rendu par image

Sur la plupart des architectures GPU mobiles, les opérations consistant à commencer et à terminer une passe de rendu sont coûteuses. Votre application peut améliorer ses performances en organisant les opérations de rendu de façon à effectuer le moins de passes possible.

Différentes opérations de chargement et de stockage de pièces jointes offrent des niveaux de performance différents. Par exemple, si vous n'avez pas besoin de conserver le contenu d'une pièce jointe, vous pouvez utiliser les opérations VK_ATTACHMENT_LOAD_OP_CLEAR ou VK_ATTACHMENT_LOAD_OP_DONT_CARE, qui sont beaucoup plus rapides, plutôt que VK_ATTACHMENT_LOAD_OP_LOAD. De même, si vous n'avez pas besoin d'écrire en mémoire les valeurs finales d'une pièce jointe pour une utilisation ultérieure, vous pouvez utiliser VK_ATTACHMENT_STORE_OP_DONT_CARE pour obtenir de bien meilleures performances qu'avec VK_ATTACHMENT_STORE_OP_STORE.

En outre, pour la plupart des passes de rendu, votre application n'a pas besoin de charger ni de stocker la pièce jointe de profondeur/gabarit. Dans ce cas, vous pouvez éviter d'allouer de la mémoire physique pour la pièce jointe en utilisant l'option VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT lors de la création de son image. Ce bit offre les mêmes avantages que glFramebufferDiscard dans OpenGL ES.

Sélectionner les types de mémoire appropriés

Lorsqu'elles allouent la mémoire de l'appareil, les applications doivent sélectionner un type de mémoire. Celui-ci détermine la manière dont une application peut utiliser la mémoire et décrit les propriétés de mise en cache et de cohérence de la mémoire. Les types de mémoire disponibles varient selon les appareils. Chaque type de mémoire présente des caractéristiques de performances différentes.

Une application peut utiliser un algorithme simple afin de sélectionner le meilleur type de mémoire pour une utilisation donnée. Cet algorithme sélectionne dans le tableau VkPhysicalDeviceMemoryProperties::memoryTypes le premier type de mémoire qui répond à deux critères : il doit être autorisé pour le tampon ou l'image, et il doit avoir les propriétés minimales requises par l'application.

Les systèmes mobiles ne disposent généralement pas de tas de mémoire physique distincts pour le CPU et le GPU. Sur ces systèmes, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT n'est pas aussi important que sur les systèmes disposant de GPU distincts dotés de leur propre mémoire dédiée. L'application ne doit pas supposer que cette propriété est obligatoire.

Regrouper les ensembles de descripteurs par fréquence

Si vous disposez de liaisons de ressources qui changent à des fréquences différentes, utilisez plusieurs ensembles de descripteurs par pipeline au lieu de lier à nouveau toutes les ressources pour chaque dessin. Par exemple, vous pouvez utiliser un ensemble de descripteurs pour les liaisons par scène, un autre pour les liaisons par matière et un troisième pour les liaisons par instance de maillage.

Utilisez des constantes immédiates pour les modifications les plus fréquentes, telles que celles exécutées à chaque appel de dessin.