Que recherchez-vous ?
Engine & platform

Optimisation des performances de chargement : Comprendre le pipeline de téléchargement asynchrone

JOSEPH SCHEINBERG / UNITY TECHNOLOGIESContributor
Oct 8, 2018|7 Min
Optimisation des performances de chargement : Comprendre le pipeline de téléchargement asynchrone
Cette page a été traduite automatiquement pour faciliter votre expérience. Nous ne pouvons pas garantir l'exactitude ou la fiabilité du contenu traduit. Si vous avez des doutes quant à la qualité de cette traduction, reportez-vous à la version anglaise de la page web.

Personne n'aime les écrans de chargement. Saviez-vous que vous pouvez ajuster rapidement les paramètres du pipeline de téléchargement asynchrone (AUP) pour améliorer considérablement vos temps de chargement ? Cet article détaille comment les maillages et les textures sont chargés via l'AUP. Cette compréhension pourrait vous aider à accélérer considérablement le temps de chargement : certains projets ont vu leurs performances augmenter de plus de 2 fois !

Lisez la suite pour découvrir comment fonctionne l’AUP d’un point de vue technique et quelles API vous devriez utiliser pour en tirer le meilleur parti.

Essayez-le

La dernière implémentation la plus optimale du pipeline de téléchargement d'actifs est disponible dans la version bêta 2018.3.

Téléchargez la version bêta 2018.3 dès aujourd'hui

Commençons par examiner en détail quand l’AUP est utilisé et comment fonctionne le processus de chargement.

Quand le pipeline de téléchargement asynchrone est-il utilisé ?

Avant 2018.3, l'AUP ne gérait que les textures. À partir de la version bêta 2018.3, l'AUP charge désormais les textures et les maillages, mais il existe quelques exceptions. Les textures activées en lecture/écriture, ou les maillages activés en lecture/écriture ou compressés, n'utiliseront pas l'AUP. (Notez que Texture Mipmap Streaming, qui a été introduit en 2018.2, utilise également AUP.)

Comment fonctionne le processus de chargement

Pendant le processus de construction, l'objet de texture ou de maillage est écrit dans un fichier sérialisé et les données binaires volumineuses (données de texture ou de vertex) sont écrites dans un fichier .resS d'accompagnement. Cette disposition s'applique à la fois aux données des joueurs et aux ensembles d'actifs. La séparation des données objet et binaire permet un chargement plus rapide du fichier sérialisé (qui contiendra généralement de petits objets) et permet un chargement simplifié des données binaires volumineuses du fichier .resS par la suite. Lorsque l'objet Texture ou Mesh est désérialisé, il soumet une commande à la file d'attente de commandes de l'AUP. Une fois cette commande terminée, les données de texture ou de maillage ont été téléchargées sur le GPU et l'objet peut être intégré sur le thread principal.

Une fois cette commande terminée, les données de texture ou de maillage ont été téléchargées sur le GPU et l'objet peut être intégré sur le thread principal

Pendant le processus de téléchargement, les données binaires volumineuses du fichier .resS sont lues dans une mémoire tampon en anneau de taille fixe. Une fois en mémoire, les données sont téléchargées vers le GPU de manière échelonnée dans le temps sur le thread de rendu. La taille de la mémoire tampon annulaire et la durée de la tranche de temps sont les deux paramètres que vous pouvez modifier pour affecter le comportement du système.

Le pipeline de téléchargement asynchrone comporte le processus suivant pour chaque commande :

1. Attendez que la mémoire requise soit disponible dans la mémoire tampon en anneau.

2. Lire les données du fichier source .resS vers la mémoire allouée.

3. Effectuer un post-traitement (décompression de texture, génération de collision de maillage, correction par plate-forme, etc.).

4. Télécharger de manière échelonnée dans le temps sur le fil de rendu

5. Libérer la mémoire tampon en anneau.

Plusieurs commandes peuvent être en cours d'exécution simultanément, mais toutes doivent allouer la mémoire requise à partir du même tampon annulaire partagé. Lorsque la mémoire tampon en anneau se remplit, les nouvelles commandes attendent ; cette attente n'entraîne pas de blocage du thread principal ni n'affecte la fréquence d'images, elle ralentit simplement le processus de chargement asynchrone.

Voici un résumé de ces impacts :

image
Quelles API publiques sont disponibles pour ajuster les paramètres de chargement

Pour profiter pleinement de l'AUP dans 2018.3, trois paramètres peuvent être ajustés lors de l'exécution de ce système :

  • QualitySettings.asyncUploadTimeSlice - La durée en millisecondes consacrée au téléchargement des textures et des données de maillage sur le thread de rendu pour chaque image. Lorsqu'une opération de chargement asynchrone est en cours, le système exécutera deux tranches de temps de cette taille. La valeur par défaut est 2 ms. Si cette valeur est trop petite, vous risquez d'être confronté à un goulot d'étranglement lors du téléchargement de textures/maillages par le GPU. En revanche, une valeur trop élevée peut entraîner des saccades au niveau de la fréquence d'images.
  • QualitySettings.asyncUploadBufferSize - La taille du tampon en anneau en mégaoctets. Lorsque la tranche de temps de téléchargement se produit à chaque image, nous voulons être sûrs que nous avons suffisamment de données dans la mémoire tampon en anneau pour utiliser l'intégralité de la tranche de temps. Si la mémoire tampon en anneau est trop petite, la tranche de temps de téléchargement sera raccourcie. La valeur par défaut était de 4 Mo en 2018.2 mais a augmenté de 16 Mo en 2018.3.
  • QualitySettings.asyncUploadPersistentBuffer - Introduit dans 2018.3, cet indicateur détermine si la mémoire tampon en anneau de téléchargement est désallouée lorsque toutes les lectures en attente sont terminées. L'allocation et la désallocation de ce tampon peuvent souvent provoquer une fragmentation de la mémoire, il doit donc généralement être laissé à sa valeur par défaut (true). Si vous avez vraiment besoin de récupérer de la mémoire lorsque vous ne chargez pas, vous pouvez définir cette valeur sur false.

Ces paramètres peuvent être ajustés via l'API de script ou via le menu QualitySettings.

Image du tampon persistant de téléchargement asynchrone sélectionné
Exemple de flux de travail

Examinons une charge de travail avec de nombreuses textures et maillages téléchargés via le pipeline de téléchargement asynchrone en utilisant la tranche de temps par défaut de 2 ms et une mémoire tampon en anneau de 4 Mo. Comme nous sommes en cours de chargement, nous obtenons 2 tranches de temps par image de rendu, nous devrions donc avoir 4 millisecondes de temps de téléchargement. En regardant les données du profileur, nous n’utilisons qu’environ 1,5 milliseconde. Nous pouvons également voir qu'immédiatement après le téléchargement, une nouvelle opération de lecture est émise maintenant que la mémoire est disponible dans le tampon circulaire. C'est un signe qu'un tampon annulaire plus grand est nécessaire.

Exemple de fichier lu après téléchargement

Essayons d'augmenter la mémoire tampon en anneau et puisque nous sommes dans un écran de chargement, c'est également une bonne idée d'augmenter la tranche de temps de téléchargement. Voici à quoi ressemblent un tampon en anneau de 16 Mo et une tranche de temps de 4 millisecondes :

Image de ce à quoi ressemble un tampon en anneau de 16 Mo et une tranche de temps de 4 millisecondes :

Nous pouvons maintenant voir que nous passons presque tout le temps de notre thread de rendu au téléchargement, et seulement un court laps de temps entre les téléchargements au rendu de l'image.

Vous trouverez ci-dessous les temps de chargement de l'exemple de charge de travail avec une variété de tranches de temps de téléchargement et de tailles de mémoire tampon en anneau. Les tests ont été effectués sur un MacBook Pro, Intel Core i7 à 2,8 GHz exécutant OS X El Capitan. Les vitesses de téléchargement et d'E/S varient selon les plates-formes et les appareils. La charge de travail est un sous-ensemble de l’ exemple de projet Viking Village que nous utilisons en interne pour les tests de performances. Étant donné que d'autres objets sont en cours de chargement, nous ne sommes pas en mesure d'obtenir le gain de performance précis des différentes valeurs. On peut cependant dire sans se tromper que dans ce cas, le chargement de la texture et du maillage est au moins deux fois plus rapide lors du passage des paramètres 4 Mo/2 MS aux paramètres 16 Mo/4 MS.

L’expérimentation avec ces paramètres produit les résultats suivants.

Image des temps de chargement moyens

Pour optimiser les temps de chargement de cet exemple de projet particulier, nous devons donc configurer les paramètres comme ceci :

Type de bloc inconnu « codeBlock », veuillez spécifier un sérialiseur pour celui-ci dans la propriété « serializers.types »

Plats à emporter et recommandations

Recommandations générales pour optimiser la vitesse de chargement des textures et des maillages :

  • Choisissez le QualitySettings.asyncUploadTimeSlice le plus grand qui n'entraîne pas de perte d'images.
  • Pendant le chargement des écrans, augmentez temporairement QualitySettings.asyncUploadTimeSlice.
  • Utilisez le profileur pour examiner l’utilisation des tranches de temps. La tranche de temps apparaîtra sous le nom AsyncUploadManager.AsyncResourceUpload dans le profileur. Augmentez QualitySettings.asyncUploadBufferSize si votre tranche de temps n'est pas pleinement utilisée.
  • Les choses se chargeront généralement plus rapidement avec un QualitySettings.asyncUploadBufferSize plus grand, donc si vous pouvez vous permettre la mémoire, augmentez-la à 16 Mo ou 32 Mo.
  • Laissez QualitySettings.asyncUploadPersistentBuffer défini sur true, sauf si vous avez une raison impérieuse de réduire votre utilisation de la mémoire d'exécution sans chargement.
FAQ

Q: À quelle fréquence le téléchargement en tranches de temps se produira-t-il sur le thread de rendu ?

  • Le téléchargement en tranches de temps se produira une fois par image de rendu, ou deux fois lors d'une opération de chargement asynchrone. VSync affecte ce pipeline. Pendant que le thread de rendu attend une VSync, vous pouvez être en train de télécharger. Si vous exécutez des images de 16 ms et qu'une image dure ensuite longtemps, disons 17 ms, vous finirez par attendre la synchronisation verticale pendant 15 ms. En général, plus la fréquence d'images est élevée, plus les tranches de temps de téléchargement se produisent fréquemment.

Q: Qu'est-ce qui est chargé via l'AUP ?

  • Les textures qui ne sont pas activées en lecture/écriture sont téléchargées via l'AUP.
  • Depuis 2018.2, les mipmaps de texture sont diffusés via l'AUP.
  • À partir de la version 2018.3, les maillages sont également téléchargés via l'AUP à condition qu'ils ne soient pas compressés et qu'ils ne soient pas activés en lecture/écriture.

Q: Que faire si la mémoire tampon en anneau n’est pas suffisamment grande pour contenir les données en cours de téléchargement (par exemple, une texture très volumineuse) ?

  • Les commandes de téléchargement dont la taille est supérieure à celle de la mémoire tampon en anneau attendront que la mémoire tampon en anneau soit entièrement consommée, puis la mémoire tampon en anneau sera réallouée pour s'adapter à la grande allocation. Une fois le téléchargement terminé, la mémoire tampon annulaire sera réaffectée à sa taille d'origine.

Q: Comment fonctionnent les API de chargement synchrone ? Par exemple, Resources.Load, AssetBundle.LoadAsset, etc.

  • Les appels de chargement synchrones utilisent l'AUP et bloqueront essentiellement le thread principal jusqu'à ce que l'opération de téléchargement asynchrone soit terminée. Le type d'API de chargement utilisé n'est pas pertinent.
Dites-nous ce que vous pensez

Nous sommes toujours à la recherche de commentaires. Dites-nous ce que vous en pensez dans les commentaires ou sur le forum bêta d’Unity 2018.3 !