> Chantier ouvert le 2026-10-02, après la migration (V1.1). Objectifs fixés par l'utilisateur :
> ressources obsolètes ou gourmandes, goulots d'étranglement, organisation des dossiers, optimisations
> propres à UE5, suppression du superflu, outils et exemples Fab.
> Ce document est le **lot O0** : mesures en lecture seule, aucune modification du projet. Les lots O1
> et suivants (§6) suivent `10_Protocole_Operations.md`.
---
## 1. Méthode et outils
| Mesure | Outil | Résultat |
|---|---|---|
| Inventaire des 8 563 assets (classe, taille, tags, références) sans chargement | `Tools/AuditAssets.py` (registre d'assets, exécuté par `-run=pythonscript`) | `Saved/Audit/assets.jsonl` |
| Noms d'assets cités par les données et les Blueprints | croisement avec le texte de `Content/Modules` et les tables de noms de `BPs`, `UI`, `Scenes` | `Saved/Audit/candidats_jamais_nommes.json` |
Les vêtements portent chacun une copie des morphs du corps pour suivre la morphologie : c'est le
premier poste de taille, et un coût par personnage à l'exécution (§4).
Textures :
- **835 textures non streamées** (`NeverStream`) et **794 sans mipmaps**, dont 541 icônes de
`New3/UI` (normal pour de l'interface).
- Le reste concerne des textures 3D (`Clothes4/Materials` 43, `New2/Furniture` 41, `Daz/Michael` 34,
`New4/Hairs` 17, `New2/Beasts` 13) : sans mipmaps, elles scintillent à distance et occupent toujours
leur pleine résolution en mémoire vidéo.
- Toutes les textures sont dans le groupe `World`, y compris l'interface ; seules 37 sont en
compression normal map.
## 3. Build livré (Shipping V1.1 : 3,17 Go)
| Poste | Taille | Constat |
|---|---:|---|
| `LifePlay/Content/Heavy` (cuit) | 2,50 Go | paquets **non compressés** (`bCompressed=False`, pas de pak) |
| `LifePlay-Win64-Shipping.pdb` | **207 Mo** | symboles de débogage livrés par erreur (inutiles au joueur) |
| `LifePlay-Win64-Shipping.exe` | 140 Mo | inclut tous les plugins moteur activés |
| `Engine/Plugins/NNE` (débruiteur IA, ONNX Runtime) | **119 Mo** | inutilisé par LifePlay ; activé par défaut par le moteur |
| `DirectML.dll`, `d3d12SDKLayers.dll` | 19 Mo | DirectML : dépendance de NNE ; SDK Layers : couche de débogage D3D12 |
| `Engine/Plugins/Interchange` | 12 Mo | import d'assets, inutile en jeu |
| `LifePlay/Content/Movies` | 79 Mo | vidéo d'intro |
| `LifePlay/Content/Modules` | 14 Mo | données texte (doivent rester en fichiers nus) |
Plugins moteur présents dans le build sans usage connu en jeu : `NNE`, `Interchange`, `Fab`, `Bridge`,
`SpeedTreeImporter`, `MovieRenderPipeline`, `Paper2D`, `USDCore`, `HairStrands`… Plusieurs viennent des
dépendances de `McpAutomationBridge` (outil d'éditeur, ERR-024) ou de l'activation par défaut du moteur.
## 4. Goulots probables (à confirmer par la mesure du §5)
| # | Hypothèse | Indice | Mesure qui tranchera |
|---|---|---|---|
| G1 | **Coût CPU des personnages** : jusqu'à des centaines de PNJ générés, chacun avec corps, cheveux et vêtements à morphs, animés à chaque image même hors champ | 339 personnages générés en 2 h (V1) ; aucun LOD, aucune optimisation de fréquence d'animation | temps du thread de jeu et d'animation (CSV) selon le nombre de PNJ visibles |
| G2 | **Morph targets** : 45 936 sur les vêtements, recalculés pour chaque personnage | `bridget_gp` 1 070 morphs | temps GPU/CPU de déformation, mémoire `Meshes` (LLM) |
| G3 | **Tick des Blueprints**`GI` et `Person` (interpréteur du DSL, IA) | 606 fonctions dans `GI` | temps du thread de jeu hors animation |
| G4 | **Mémoire vidéo** : textures non streamées et maillages sans LOD | §2 | LLM `Textures`, `Meshes` |
| G5 | **Temps de chargement** : 2,5 Go de paquets non compressés, chargés par chemin | build Shipping | durée du démarrage et des changements de lieu (journal) |
## 5. Mesure en jeu à réaliser
Dans `D:\Game\LifePlay_Build_5.5_L7cdj\Windows`, lancer **`LifePlay_profil_O0.bat`** (et non
`LifePlay.exe`) puis jouer environ **15 minutes** :
1. charger une partie avancée (nombreux contacts), rester 2 minutes sur la carte de la ville ;
2. un lieu intérieur peuplé, 2 minutes ;
3. un lieu extérieur peuplé (parc, plage), 2 minutes ;
4. une scène avec plusieurs personnages ;
5. quitter normalement.
Résultats : `Windows\LifePlay\Saved\Profiling\CSV\*.csv` (temps par image, thread de jeu, rendu, GPU,
par tranche de 9 000 images) et `Saved\Profiling\LLM\*.csv` (mémoire par catégorie). Ils serviront de
**référence** : chaque lot O1+ sera mesuré à l'identique, avant et après.
## 6. Leviers classés et lots proposés
| Lot | Levier | Gain attendu | Risque | Changement de comportement |
|---|---|---|---|---|
| **O1** | Build : désactiver les plugins moteur inutiles au jeu (`NNE`, `Interchange`, `Fab`, `Bridge`…), ne plus livrer le `.pdb` ni les couches de débogage D3D12 | **≈ 350 Mo** sur 3,17 Go, exécutable plus petit, démarrage plus court | faible (vérifier que `ControlRig`, `IKRig`, `HairStrands` ne sont pas utilisés par les assets) | aucun |
| **O2** | Paquets compressés (IoStore + Oodle) pour `Content`, `Modules` restant en fichiers nus | taille du jeu et temps de chargement (à mesurer : souvent −30 à −50 %) | moyen : la version de l'auteur était en fichiers nus ; vérifier que les mods n'ajoutent que du texte | aucun pour le joueur |
| **O3** | Fréquence d'animation des PNJ : `VisibilityBasedAnimTickOption` (pas d'animation hors champ), optimisation de fréquence (URO), voire Animation Budget Allocator. Appliqué par un sous-système C++, sans modifier `Person` | thread de jeu (G1), proportionnel au nombre de PNJ | faible | PNJ hors champ figés (invisible au joueur) |
| **O4** | Textures 3D sans mipmaps (≈ 130) : génération des mipmaps et streaming ; textures d'interface dans le groupe `UI` | mémoire vidéo, scintillement à distance | faible | rendu plus net à distance |
| **O5** | LOD des maillages squelettiques (corps, cheveux, vêtements) par la réduction intégrée d'UE5 | GPU et mémoire avec beaucoup de PNJ | moyen : les morphs doivent suivre sur les LOD ; contrôle visuel | légère simplification à distance |
| **O6** | Morph targets inutilisés des vêtements : retirer ceux qu'aucune donnée ni aucun Blueprint ne pilote | taille (2,4 Go de vêtements) et coût de déformation | élevé : il faut la liste exacte des morphs pilotés (`.lpcharacter`, `MorphTags`, `Person`) | aucun si la liste est exacte |
| **O7** | Organisation : déplacer les sauvegardes de développement de `Content/Saves` (14 125 fichiers, 239 parties, non versionnées) hors du projet ; plan de nommage des dossiers `Heavy/New`, `New2`… | lisibilité, éditeur plus rapide à indexer | les déplacements d'assets demandent des redirecteurs et un contrôle des 5 chemins `/Game/` des données | aucun |
| **O8** | Supprimer les 258 assets jamais nommés ni référencés (animations `Daz/Anims`, icônes `New3/UI`…) | **60 Mo seulement** (1 %) | moyen : un nom peut être construit par concaténation dans un Blueprint | aucun si l'analyse est juste |
Conclusion de l'audit statique : **supprimer des assets rapporte peu** (O8). Les vrais gains de taille
sont dans le **build** (O1, O2) et dans les **morphs des vêtements** (O6). Les gains de fluidité sont
dans **l'animation et la déformation des personnages** (O3, O5, O6), à confirmer par la mesure du §5.
Ordre recommandé : O1 (sans risque, gain immédiat), puis mesure §5, puis O3 et O4, puis O2, O5, O6 selon
> Lancement : la capture CSV doit être démarrée par `-ExecCmds="csvprofile repeat=20, csvprofile frames=9000"`.
> L'option `-csvCaptureFrames` sur la ligne de commande démarre la capture avant l'initialisation du rendu, et le
> moteur s'arrête (`IsRayTracingAllowed() may only be called once RHI is initialized`).
### 9.1 Régime normal : aucun goulot
| Mesure (médiane par tranche) | Valeur |
|---|---|
| Temps d'image | **16,7 ms** : le jeu est plafonné à 60 images/s et tient ce plafond |
| Thread de jeu | 3,9 à 4,4 ms |
| Thread de rendu | 7,8 à 8,5 ms (dont ≈ 9 ms d'attente, `EventWait`) |
| GPU | 7,3 à 11,1 ms |
Il reste environ deux fois de marge sur chaque thread. Les hypothèses G1, G2 et G4 (§4) ne se vérifient
pas en régime normal sur cette machine.
### 9.2 Le vrai problème : les à-coups du thread de jeu
| Seuil | Images concernées (sur 36 004) |
|---|---:|
| > 33 ms | 262 |
| > 50 ms | 159 |
| > 100 ms | 112 |
| > 250 ms | 43 |
| > 1 s | 3 (chargement de partie : 31,5 s ; application des réglages : 5,4 s ; 1,7 s) |
- Les images longues durent **de 0,5 à 0,9 s**, à intervalles irréguliers, plusieurs fois par minute ; le
thread de jeu en est la cause, le rendu restant à 8 ms.
- Dans ces images, le temps va aux **minuteries Blueprint** (`TimerManager`, 38 s cumulées) et aux nœuds
`Delay` (`FlushLatentActions`, 9 s cumulées).
- Ce sont les traitements de `GI` déclenchés par minuterie : passage du temps, IA, recherche de scènes,
génération de personnages avec lecture de fichiers texte et chargement synchrone de maillages.
**Nouvelle priorité : G6, à-coups des traitements Blueprint sur minuterie.** Le CSV ne nomme pas les
fonctions Blueprint. L'étape suivante est une trace Unreal Insights avec les événements nommés
(`-trace=cpu,frame,loadtime -statnamedevents`), pour identifier les fonctions de `GI` en cause et les
chargements synchrones.
### 9.3 Mémoire (fin de session, LLM)
| Catégorie | Mo |
|---|---:|
| Total processus | 3 546 |
| Textures | 624 |
| **Physique (Chaos)** | **463**, dont 124 de structures d'accélération et 92 de maillages de collision (trimesh) |
| UObject | 226 |
| Cibles de rendu | 215 |
| Maillages | 75 |
La physique occupe presque autant que les textures. Hypothèse : collisions détaillées du maillage de la
ville (`CentralLondon`, 1,08 million de triangles) et des meubles. Ce poste devient le levier mémoire n° 1.
### 9.4 Lots révisés
| Ordre | Lot | Justification |
|---|---|---|
| 1 | **O1** build (plugins, `.pdb`) | inchangé : gain sûr de ≈ 350 Mo |
| 2 | **O9** (nouveau) : trace Insights des à-coups, puis correction ciblée (chargement asynchrone, étalement des traitements) | seul défaut ressenti en jeu |
| 3 | **O10** (nouveau) : collisions (ville et meubles) | 463 Mo de physique |
| Assets Auto Organizer | Beta Projects | réorganisation des dossiers (O7) : **à n'utiliser qu'avec redirecteurs et vérification**, le jeu cherchant les assets par nom |
| Asset Naming Convention Keeper ; Asset Sanitizer | Tobosoksini Games ; MallHockE | conventions de nommage (O7) |
S'y ajoutent 17 chemins complets dans `Content/Modules` (`New2`, `New3`). Les modules et les mods de la communauté
désignent meubles, vêtements et cheveux **par leur nom**, dans ces dossiers : leur emplacement fait partie du contrat
de modding.
### 16.3 Options
| Option | Contenu | Risque | Recommandation |
|---|---|---|---|
| **O7a, ménage** | `Content/Saves` (239 parties de test de l'éditeur, 85 Mo, non versionnées, non livrées) déplacées vers la sauvegarde ; dossiers vides `Content/Developers` et `Content/Collections` retirés ; 3 captures de diagnostic à la racine déplacées vers `D:\Game\LifePlay_Backup_5.5` | nul | **oui** |
| **O7b, carte documentée** | §16.1 et §16.2 portés dans `08_Guide_Developpeur.md` : ce que contient chaque dossier, et quels dossiers ne doivent pas bouger | nul | **oui** |
| **O7c, renommage réel** | renommer `New`, `New2`… par thème (`Furniture`, `Animations`, `Hair`…), corriger les 10 préfixes dans 6 Blueprints et les 17 chemins des données | élevé : mods de la communauté cassés, comparaisons avec 4.27 perdues, des milliers de paquets réenregistrés | **non** (gain seulement cosmétique) |
### 16.4 Réalisé (2026-10-02)
- **O7a** :
-`Content/Saves` vidé : 14 125 fichiers (239 parties de test) déplacés vers
`D:\Game\LifePlay_Backup_5.5\O7\Content_Saves`, nombre de fichiers et d'octets vérifiés identiques ; le dossier
est conservé, vide ;
-`Content/Developers` et `Content/Collections` retirés : ils ne contenaient que des dossiers vides, que l'éditeur
recrée ;
- 3 captures de diagnostic de la racine rangées dans `D:\Game\LifePlay_Backup_5.5\O7\racine`.
- **O7b** : carte des dossiers et liste des dossiers à ne pas déplacer dans `08_Guide_Developpeur.md` §2.1.
### 16.6 O7e : intégrer une coiffure de Fab — étude (2026-10-02, lecture seule)
Test d'intégration proposé par l'utilisateur : ajouter une coiffure de sa bibliothèque Fab. Relevés : `LPDumpFunction`
(`GI`, `Person`, `UI/Hairstyle`, tous graphes) et `LPDumpCDO` (`GI_C`).
**Contrat d'une coiffure LifePlay**
| Point | Constat |
|---|---|
| Liste des coiffures | `GI.HairstyleTable` : **valeur par défaut** du Blueprint (126 entrées), jamais construite par le jeu. Chaque entrée (`SMBool`) : chemin complet du maillage, `Bool` = coiffure masculine, `Daz` = coiffure Daz, `Strings` (zones de pilosité faciale). 88 entrées existent ; 35 coiffures d'avant Daz pointent vers des assets absents, déjà en 4.27, et ne sont jamais chargées (`LegacyPercentage` = 0). |
| Choix d'une coiffure | `GenerateAndApplyStrings`, catégorie `Hairstyle` : parcourt `HairstyleTable` et compare le **nom de l'objet** à la valeur lue (`HAIR Hairstyle: ToulouseHR`) ; tirage aléatoire parmi les entrées du bon sexe. Un maillage absent de la table n'est donc jamais utilisé, même désigné par nom. |
| Chargement | `GI.LoadHairMeshIfNeeded` : chargement à la demande depuis le chemin de la table. |
| Animation | `Person.HairSM.SetLeaderPoseComponent(corps)` : la coiffure suit le corps **par noms d'os**. Les coiffures existantes sont des `SkeletalMesh` à squelette propre de 20 à 27 os Genesis 8 (`hip`, `pelvis`, `abdomenLower`… `neckUpper`, `head`). |
| Forme | `Person.FitMorphableHair` (table `GI.MorphableHairs`) reporte les morphs du corps et du visage sur la coiffure (146 à 362 morphs par coiffure, ex. `ToulouseHR__CTRLFitness`). Sans ces morphs, la coiffure garde sa forme d'origine (risque de traverser une tête modifiée). |
| Couleur | paramètres de matériau `HairColor` et `HairThickness` (menu `UI/Hairstyle`, `GI.GetHairThicknessAndColor`). |
| Sauvegarde | les personnages (`.lpcharacter`) stockent la coiffure **par nom** : ajouter une entrée en fin de table ne change rien aux parties existantes. |
**Bibliothèque Fab** : 34 coiffures. Les « grooms » MetaHuman (≈ 18) sont exclus : liés à la tête MetaHuman, et le
plugin `HairStrands` n'est plus actif dans le jeu depuis O1. Restent ≈ 12 maillages FBX/OBJ (Pocolov Hair 05, 06, 16,
17 ; Laflian ; NoEdge ; Carlos-H). Pocolov publie des coiffures Daz ; le rig Genesis du FBX reste à constater.
**Intégration proposée (O7e)**
1. Télécharger un produit (accord utilisateur), importer dans `/Game/Heavy/New5/Hairs/` (hors chemins fixes).
2. Si le FBX n'est pas riggé Genesis 8 : le rattacher à une chaîne d'os Genesis (poids entier sur `head`), squelette
propre comme les coiffures existantes ; matériau enfant d'un matériau de cheveux existant (`HairColor`,
`HairThickness`).
3. Ajouter une entrée en fin de `GI.HairstyleTable` par une commandlet dédiée (même méthode que
**Où passe le temps de l'analyse** (statistiques limitées à la fenêtre de chaque événement) :
-`PopulateScenes` : **94 % dans une seule fonction, `WhatWhereToArray`** (3,43 s pour 1 558 appels, un par scène).
Elle développe les listes `WHAT:`/`WHERE:` (par exemple `all, -sleep, -nap`) en parcourant pour chaque scène les
tableaux de structures `ActionDetail` et `AmenityAction`. Cela fait 503 000 lectures d'éléments de tableau, chaque
structure étant copiée par le Blueprint (2,18 s), et 1,27 million de comparaisons de texte (0,55 s).
-`PopulateActions` : boucle de l'EventGraph sur les lignes des actions. 473 000 lectures d'éléments (0,94 s), et
≈ 234 000 appels chacun à `Trim`, `StartsWith`, `EndsWith` et à des comparaisons (≈ 0,6 s).
**Options** :
| Option | Contenu | Gain par passage | Effort | Risque |
|---|---|---:|---|---|
| **4a** | ne plus relancer `PopulateRaws` à la fermeture du gestionnaire de mods si la liste des mods actifs est inchangée (branche ajoutée dans `GI` par commandlet, contrôle de diff) | **≈ 5,5 s** à chaque fermeture sans changement | 1 lot | faible |
| **4b** | `WhatWhereToArray` réécrite en C++ (`LPDataCompat`, même signature), nœuds rebranchés par commandlet comme les correctifs CX-B ; preuve d'équivalence : commandlet qui exécute les deux versions sur les 1 558 en-têtes et compare | **≈ 3,3 s** à chaque passage | 1 à 2 lots | moyen (lecture des structures Blueprint par réflexion) |
| 4c | boucle de `PopulateActions` en C++ | ≈ 1,5 s | 1 à 2 lots | moyen (logique au milieu de l'EventGraph) |
| 4d | réécriture complète des analyseurs (étape 4 d'origine) | — | 10 à 15 lots | **abandonnée** : le coût est concentré dans 4b et 4c |
Ordre recommandé : 4b (le plus gros gain, à chaque chargement de partie), puis 4a, puis 4c si nécessaire.