Mécanisme de rendu 3DGS et gestion de la mémoire vidéo
Ce document décrit comment les données entrent en mémoire vidéo pour participer au rendu, ainsi que les règles d'allocation et les limites de capacité de la mémoire vidéo. Pour le réglage des paramètres d'image, voir Réglages visuels. Pour les opérations de chargement, voir Démarrage rapide.
Terminologie : la plus petite unité de chargement et de rendu obtenue après découpage des données est appelée nœud (Node). La précision de chaque nœud est identifiée par son Level : le Level 0 correspond à la précision la plus élevée, et plus le Level est grand, plus la précision est basse. Ces définitions correspondent à celles de Paramètres de performance.
Rendu par blocs et rendu à chargement complet
Une fois les données chargées, deux modes de rendu sont possibles : charger les nœuds visibles à la demande (rendu par blocs), ou charger l'intégralité du jeu de données d'un coup (rendu à chargement complet). Le comportement par défaut diffère selon la pipeline :
Note : « par blocs » signifie ici que les données sont découpées en nœuds spatiaux et chargées à la demande, ce qui diffère de la rastérisation Tiled où les tuiles sont des tuiles de pixels écran. L'un décide comment les données entrent en mémoire vidéo, l'autre comment les données déjà présentes sont dessinées. Les deux sont indépendants.
| Pipeline | Formats | Mode par défaut |
|---|---|---|
| LCC2 | .lcc2 / .ply / .spz / .sog | Rendu par blocs |
| LCC | .lcc | Rendu à chargement complet ; revient au rendu par blocs quand les données dépassent le seuil |
Rendu par blocs
Les données sont découpées en nœuds spatiaux, et chaque nœud contient plusieurs Levels. Chaque image est traitée ainsi :
Traversal (filter visible nodes by camera position and orientation, pick a Level for each node)
│
▼
Load (read the data of the selected nodes into memory)
│
▼
Upload (submit the data to video memory)
│
▼
Render
Les nœuds proches utilisent un Level bas, les nœuds éloignés un Level élevé, et les nœuds invisibles ne sont pas chargés. Un nœud qui quitte le champ de vision n'est pas libéré immédiatement : il reste en cache pour être réutilisé directement lorsqu'il revient dans le champ. Ce n'est qu'en cas de manque de mémoire vidéo que les nœuds les moins récemment utilisés sont évincés, selon des critères comme la date du dernier rendu. L'occupation de la mémoire vidéo dépend surtout de l'étendue visible à l'instant présent, ce qui permet de gérer de grandes scènes.
Les deux pipelines soumettent les dessins différemment :
| Pipeline | Soumission des dessins |
|---|---|
| LCC | Chaque nœud est soumis comme un dessin distinct, donc les draw calls augmentent avec le nombre de nœuds |
| LCC2 | Les données de tous les nœuds visibles sont fusionnées dans un seul Buffer et soumises en un seul dessin |
Coûts inhérents au rendu par blocs :
- Des nœuds adjacents peuvent recevoir des Levels différents, donc la densité et la netteté diffèrent à la frontière et les bords de nœuds deviennent visibles
- Les Levels changent en permanence pendant les déplacements de caméra, ce qui peut provoquer des apparitions brusques de détails
Rendu à chargement complet
L'étape de filtrage des nœuds par la caméra est ignorée. L'ensemble du jeu de données est chargé d'un coup et reste résident en mémoire vidéo, donc déplacer la caméra ne déclenche plus ni traversal ni sélection de Level.
Avantages :
- Toute la scène utilise un seul Level, donc pas de différence de Level entre nœuds ni de bords de nœuds visibles
- Les données restent résidentes en mémoire vidéo, donc les déplacements de caméra n'entraînent ni chargement, ni libération, ni apparition brusque de détails
- Le coût de traversal et d'upload par image disparaît, ce qui donne une fréquence d'images plus stable
En contrepartie, toutes les données restent résidentes en mémoire vidéo et l'occupation croît linéairement avec la taille de la scène ; cela ne convient donc qu'aux petites scènes.
Critères du rendu à chargement complet
L'utilisation du rendu à chargement complet est déterminée au moment du chargement par l'option Use Full Load et le nombre de splats de Level 0 :
Load(data file)
│
▼
Read the Level 0 splat count
│
▼
Use Full Load enabled? ── No ──▶ Chunked rendering
│ Yes
▼
Splat count ≤ Full Load Splat Number? ── No ──▶ Chunked rendering
│ Yes
▼
Full load rendering
Les deux propriétés se trouvent dans la catégorie Performance du panneau Details de l'Actor :
| Propriété | Description |
|---|---|
| Use Full Load | Autorise ou non le rendu à chargement complet |
| Full Load Splat Number | Limite supérieure du nombre de splats de Level 0 autorisant le rendu à chargement complet (unité : 10 000, valeur par défaut 1500) |
Les valeurs par défaut diffèrent selon le type d'Actor :
| Actor | Valeur par défaut de Use Full Load | Raison |
|---|---|---|
| ALCCActor | Activé | Les différences de Level entre nœuds sont marquées et les bords de nœuds visibles, et chaque nœud coûte un draw call ; le rendu à chargement complet donne donc un meilleur rendu dans les petites scènes |
| ALCC2Actor / APlyActor / ASpzActor / ASogActor | Désactivé | Le changement de Level se fait de façon continue selon l'erreur en espace écran et les bords de nœuds sont discrets, et un seul draw call est nécessaire ; le chargement à la demande économise donc davantage de mémoire vidéo par défaut |
Quand activer manuellement le rendu à chargement complet sur LCC2
La pipeline LCC2 désactive le rendu à chargement complet par défaut. Activez-le manuellement dans ces cas :
- La scène est de taille limitée, la mémoire vidéo est suffisante, et la qualité d'image comme la fréquence d'images doivent rester stables
- La caméra se déplace vite ou se téléporte souvent, le chargement des nœuds ne suit pas, et des trous ou des apparitions brusques de détails apparaissent
- Présentations produit, captures vidéo, rendu de séquences et autres cas où des variations d'image pendant le chargement sont inacceptables
- Les différences de densité aux frontières de nœuds restent visibles
Si la mémoire vidéo devient juste ou si la fréquence d'images chute après activation, la scène dépasse le périmètre adapté au rendu à chargement complet : revenez au rendu par blocs.
Note : le rendu à chargement complet reste soumis à la distance de rendu maximale. Rien n'est affiché quand la distance entre la caméra et les données dépasse cette valeur.
Budget de mémoire vidéo et limites de capacité
La pipeline LCC2 fusionne les données de splats résidentes dans un seul Buffer pour le rendu ; la quantité de données qu'un modèle unique peut charger dépend donc des limites de capacité de ces Buffers.
Structure des Buffers et coût par splat
Les données sont stockées dans deux Buffers :
| Buffer | Coût par splat | Contenu |
|---|---|---|
| Buffer de données gaussiennes de base | 40 octets | Position, rotation, couleur, échelle |
| Buffer d'harmoniques sphériques | 12 / 27 / 48 octets | Coefficients d'harmoniques sphériques, alloués uniquement pour les données qui en contiennent |
Le Buffer d'harmoniques sphériques est alloué selon le nombre de bandes réellement stocké dans les données : 12 octets pour la bande 1, 27 octets pour la bande 2 et 48 octets pour la bande 3. Les données de bande inférieure ne réservent pas d'espace pour la bande la plus élevée, donc la même quantité de mémoire vidéo contient plus de splats.
Limite absolue d'un seul Buffer
Unreal enregistre les tailles de Buffer sous forme d'entiers 32 bits, ce qui donne une limite théorique de 4 GB. Le plugin utilise en pratique 4095 MB (1 MB de moins que 4 GB) ; cette marge réservée évite un dépassement lors du calcul des octets pendant l'upload. Cette limite s'applique à chaque Buffer séparément et ne peut pas être relevée par un réglage.
Réglage du budget de mémoire vidéo
La mémoire vidéo qu'un seul modèle .lcc2 peut garder résidente est contrôlée par LCC2 GPU Memory Budget (MB) dans ProjectSettings > Plugins > LCC4Unreal :
| Élément | Valeur |
|---|---|
| Valeur par défaut | 2048 MB |
| Plage réglable | 2048 ~ 8192 MB |
| Prise d'effet | Redémarrer l'éditeur après modification |
Le budget est le total des deux Buffers, pas la limite d'un seul. Les données d'harmoniques sphériques de bande 3 comptent donc pour 88 octets par splat (40 + 48), et les données sans harmoniques pour 40 octets.
Ce budget délimite la fenêtre de résidence du chargement en streaming ; il ne s'applique donc qu'à .lcc2, qui peut échanger des nœuds. Les formats à fichier unique (.ply / .spz / .sog) doivent rester entièrement résidents et suivent leurs propres règles de capacité ; voir Limites de chargement des formats à fichier unique.
Coût supplémentaire du Buffer de tri
Avant le rendu du 3DGS, les splats sont triés par leur distance à la caméra. Le résultat du tri dépend du point de vue, donc chaque vue nécessite son propre jeu de Buffers de tri. Cette part n'est pas comptée dans le budget ci-dessus : chaque splat résident coûte 16 octets supplémentaires par vue (deux copies de la clé de tri et de l'index, utilisées pour l'alternance lecture/écriture pendant le tri).
Avec le budget de 2048 MB et des données contenant des harmoniques sphériques, environ 24,4 millions de splats peuvent rester résidents, et le Buffer de tri d'une seule vue représente environ 372 MB.
L'œil gauche et l'œil droit du rendu stéréo, chaque joueur en écran partagé et chaque SceneCapture comptent chacun pour une vue. Les Buffers de tri augmentent avec le nombre de vues, et ils ne se réduisent pas après une baisse du nombre de vues ; réservez donc de la mémoire vidéo pour le nombre de vues maximal atteint.
Limites de chargement des formats à fichier unique
Les formats à fichier unique sont .ply / .spz / .sog, c'est-à-dire les formats qui contiennent l'intégralité du jeu de données dans un seul fichier, sans subdivision en nœuds ni information de Level. Ces données ne peuvent pas être chargées par parties : le plugin ne peut que les décoder en un seul nœud en une passe et tout garder résident en mémoire vidéo, il existe donc une limite précise du nombre de splats. Les formats .lcc et .lcc2 réalisent déjà la subdivision en nœuds à l'export et ne sont pas des formats à fichier unique.
Comment la limite est calculée
La limite des formats à fichier unique n'est pas bornée par LCC2 GPU Memory Budget. Ce budget dimensionne la fenêtre de résidence du chargement en streaming, ce qui n'a de sens que pour .lcc2, où les nœuds peuvent être échangés. Un fichier unique est un nœud unique qui doit rester résident en entier, et appliquer la même valeur en ferait un plafond artificiel qui refuserait des modèles que la carte graphique pourrait aisément contenir. La capacité d'un fichier unique est donc déterminée directement par le matériel, et modifier le budget n'a aucun effet dessus.
Le plugin calcule la limite avant le chargement et refuse les données qui la dépassent. La limite est la plus petite des deux conditions suivantes :
| Condition | Description |
|---|---|
| Limite d'un seul Buffer | 4095 MB divisés par le pas par splat (40 B sans harmoniques sphériques, 12 ~ 48 B avec, selon le nombre de bandes réel) |
| Mémoire vidéo réellement disponible | 90 % de la mémoire vidéo actuellement disponible sur la carte, en laissant une marge au moteur et au pilote |
La mémoire vidéo est mesurée avec le coût physique par splat, y compris les 16 octets du Buffer de tri. Sur les plateformes où la mémoire de la carte ne peut pas être interrogée, seule la limite d'un seul Buffer est utilisée, plutôt qu'une valeur estimée.
Le nombre de bandes d'harmoniques sphériques modifie directement la capacité : les données de bande 1 ont un pas d'harmoniques sphériques de 12 octets, soit un quart de la bande 3 (48 octets), donc la même carte contient nettement plus de splats. Le plugin calcule avec le nombre de bandes réellement stocké dans les données, au lieu de toujours supposer les 48 octets les plus larges.
Que se passe-t-il au-delà de la limite
Quand des données à fichier unique dépassent la limite, le chargement est refusé, et l'Output Log affiche le nombre de splats réel et la limite en cours, en indiquant si la limite provient de la restriction d'un seul Buffer ou de la mémoire vidéo disponible. Traitez le problème dans cet ordre de préférence :
- Convertissez en
.lcc2pour que le plugin charge à la demande au lieu de tout garder résident ; le volume total de données n'est alors plus soumis à cette limite. - Passez à des données avec un nombre de bandes d'harmoniques sphériques plus faible ou sans harmoniques, ce qui réduit le coût en mémoire vidéo par splat.
- Passez à une carte avec plus de mémoire vidéo. Quand la mémoire vidéo disponible est le facteur limitant, c'est la seule mesure matérielle qui relève la limite.
Passer à .sog ou .spz ne permet pas de dépasser la limite : la compression de ces formats ne concerne que le fichier sur disque, et chaque splat occupe exactement la même quantité de mémoire vidéo après décodage.
Le format LCC2 n'est pas soumis à cette limite
.lcc2 découpe les données en plusieurs nœuds, et chaque nœud est décodé et uploadé séparément. Un nœud isolé reste très en dessous des limites ci-dessus, donc le volume total de données n'a pas de limite absolue et peut largement dépasser le budget de mémoire vidéo. Ce qui est contraint, ce sont deux quantités à l'exécution :
| Quantité contrainte | Origine de la limite | Comportement en cas de dépassement |
|---|---|---|
| Splats résidents simultanément | Budget de mémoire vidéo et limite d'un seul Buffer | Les nœuds les moins récemment utilisés sont évincés et échangés |
| Splats rendus en une image | Environ 89 millions (4095 MB ÷ 48 B) | Les nœuds éloignés sont abandonnés par distance |
La quantité rendue par image est aussi limitée par Max Splat Num sur l'Actor, et la plus petite des deux valeurs s'applique.
Gestion et réglage de la mémoire vidéo
Libération automatique en cas de manque de mémoire vidéo
À l'exécution, le plugin surveille en continu l'utilisation réelle de la mémoire vidéo de la carte graphique. Quand l'utilisation dépasse le pourcentage défini par Max GPU Usage Percentage For Release dans ProjectSettings, les nœuds sont classés selon une combinaison de date du dernier rendu, fréquence d'accès, taille des données et Level, et les moins récemment utilisés sont libérés en premier. Les nœuds de Level élevé sont protégés afin d'éviter de grands trous lors d'une rotation rapide du point de vue.
Réglages associés :
| Réglage | Description |
|---|---|
| Max GPU Usage Percentage For Release | Pourcentage d'utilisation de la mémoire vidéo qui déclenche la libération automatique (50 ~ 100 %) |
| GPU Release Percentage | Pourcentage de données libérées à chaque déclenchement (10 ~ 100 %) |
| LCC2 GPU Memory Budget (MB) | Budget de mémoire vidéo pour les données de splats d'un seul modèle |
Diagnostiquer et ajuster un budget insuffisant
Un budget trop petit ne provoque pas d'échec de chargement ; à la place, les données sont échangées en permanence. Symptômes courants :
- Des trous apparaissent pendant les déplacements ou les rotations de caméra, et se remplissent progressivement à l'arrêt
- La même zone se précise et se floute à répétition quand le point de vue oscille
- Une zone locale reste floue et ne se précise pas, même en s'en approchant
- La fréquence d'images fluctue avec la quantité de contenu visible et chute nettement dans les vues dégagées
- Le comportement est normal quand la scène est statique, mais saccadé en mouvement
Un flou local est facilement pris pour une mauvaise qualité de données. Quand les données de Level bas ne peuvent pas être chargées à temps, le plugin comble le manque avec les données de Level élevé déjà présentes en mémoire vidéo pour qu'aucune partie de l'image ne manque, puis les remplace dès que les données de Level bas sont prêtes. Quand une mémoire vidéo insuffisante fait évincer les données de Level bas à répétition, cette zone reste à un Level élevé. Le test consiste à vérifier si elle se précise en s'en approchant : normalement une à deux secondes suffisent, et si rien ne change, c'est que les données de Level bas ne parviennent pas à entrer en mémoire vidéo.
Confirmation : consultez le coût prévu du modèle affiché dans l'Output Log au chargement et comparez-le au budget en cours. Si le coût prévu d'un seul modèle approche ou dépasse le budget, l'espace résident est insuffisant. Le nombre de libérations peut aussi être observé avec stat LCC ; une valeur qui ne cesse de croître indique des évictions fréquentes.
Relever le budget n'est utile que si la carte graphique a de la marge. Le budget n'est que la limite supérieure demandée par le plugin ; dès qu'il dépasse la mémoire vidéo réellement disponible sur la carte, le pilote transfère des données vers la mémoire système, et la baisse de fréquence d'images est pire que l'échange de nœuds. Procédez dans cet ordre :
- Vérifiez la mémoire vidéo disponible sur la carte. Après ajout du coût du Buffer de tri, le budget doit laisser de la marge au moteur lui-même et aux autres ressources.
- Relevez le budget par étapes (2048 → 4096 → 8192) et vérifiez après chaque redémarrage si les trous et la fréquence d'images s'améliorent.
- Si l'amélioration est faible, le facteur limitant n'est pas le budget : réduisez plutôt le volume de données, en baissant Max Splat Num, en activant la limite Max Distance ou en passant à des données sans harmoniques sphériques.
Note : le budget est calculé indépendamment par modèle. Quand plusieurs LCC Actors sont placés dans une scène, l'occupation de la mémoire vidéo s'accumule avec le nombre de modèles ; réduisez donc le budget par modèle en conséquence.