From b4c4886fc9cea01e5028cd457a432b42a8ce3fd1 Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:41:57 +0200 Subject: [PATCH] O0 : audit d'optimisation UE5 (inventaire des assets, build livre, leviers et lots proposes) Co-Authored-By: Claude Opus 5.5 --- Docs/Migration/11_Audit_Optimisation.md | 153 ++++++++++++++++++++++++ Docs/Migration/Journal_Modifications.md | 1 + Docs/Migration/README.md | 1 + Tools/AuditAssets.py | 69 +++++++++++ 4 files changed, 224 insertions(+) create mode 100644 Docs/Migration/11_Audit_Optimisation.md create mode 100644 Tools/AuditAssets.py diff --git a/Docs/Migration/11_Audit_Optimisation.md b/Docs/Migration/11_Audit_Optimisation.md new file mode 100644 index 0000000..1c36685 --- /dev/null +++ b/Docs/Migration/11_Audit_Optimisation.md @@ -0,0 +1,153 @@ +# 11 — Audit d'optimisation UE 5.5 (lot O0) + +> 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` | +| Composition du build livré | build Shipping V1.1 (`LifePlay_Build_5.5_Shipping`) | §3 | +| Performances en jeu | profileur CSV + suivi mémoire LLM : `LifePlay_profil_O0.bat` (build L7cdj) | **à faire par l'utilisateur** (§5) | + +Contrainte découverte, déterminante pour toute suppression ou réorganisation : **le jeu désigne la +plupart des assets par leur nom court** (`Clothes4.txt`, `.lpworld`, `.lpcharacter`), et les retrouve +en parcourant le registre par classe (`GetAssetsByClass`). Un asset sans référence dans le registre +peut donc être utilisé en jeu. Seules 5 lignes de données citent un chemin `/Game/` complet (lunettes +de `New3/Clothes`). + +## 2. Contenu du projet (6,44 Go, 24 912 fichiers) + +| Classe | Nombre | Taille source | Constat | +|---|---:|---:|---| +| `SkeletalMesh` | 1 057 | **4,08 Go** | 100 % à **un seul LOD** ; **54 536 morph targets** au total | +| `AnimSequence` | 1 705 | 819 Mo | `New2/Anims2` 481, `New3/Anims` 418, `New2/Daily` 344, `Daz/Anims` 313 | +| `Texture2D` | 2 969 | 810 Mo | majoritairement ≤ 1024 ; 68 en 2K/4K (visages, corps) | +| `SoundWave` | 419 | 208 Mo | presque tous dans `New2` | +| `MaterialInstanceConstant` | 1 180 | 132 Mo | — | +| `StaticMesh` | 650 | 128 Mo | 637 à un seul LOD ; `fnt_decor14` 180 000 triangles | +| Blueprints, widgets | 100 | 116 Mo | `GI` 50 Mo, `Person` 20 Mo | + +Répartition des maillages squelettiques : + +| Dossier | Maillages | Taille | Morph targets | +|---|---:|---:|---:| +| `Heavy/Clothes4` (vêtements) | 940 | 2,43 Go | **45 936** (≈ 49 par vêtement) | +| `Heavy/Daz` (corps, cheveux) | 22 | 633 Mo | 5 257 (`bridget_gp` : 1 070, 132 Mo) | +| `Heavy/New2` | 44 | 543 Mo | 1 570 | +| `Heavy/New3` | 36 | 360 Mo | 1 675 | +| `Heavy/New4` | 15 | 113 Mo | 98 | + +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 +les résultats ; O7 et O8 en dernier. + +## 7. Fonctions UE5 utiles, déjà dans le moteur + +| Fonction | Usage pour LifePlay | +|---|---| +| Unreal Insights, profileur CSV, LLM | mesure (§5) | +| Size Map, Asset Audit, Reference Viewer (éditeur) | vérifier les dépendances avant O1, O7, O8 | +| Réduction de maillage squelettique intégrée | LOD des personnages (O5) | +| Animation Budget Allocator (plugin moteur), URO, `VisibilityBasedAnimTickOption` | coût d'animation des PNJ (O3) | +| Significance Manager (plugin moteur) | prioriser les PNJ proches | +| IoStore, compression Oodle | taille et chargement (O2) | +| Texture streaming, groupes de LOD de texture | mémoire vidéo (O4) | + +Nanite ne s'applique pas aux personnages en 5.5 dans un cadre stable (maillages squelettiques) ; les +maillages statiques de LifePlay sont légers, sauf `CentralLondon` (1,08 million de triangles) et +`fnt_decor14` : Nanite pourrait être évalué pour ces deux-là seulement. + +## 8. Fab + +Recherche publique « optimization » (2026-10-02, sans connexion) : la plupart des outils sont payants +(10 à 150 €) et génériques. Ils reprennent pour l'essentiel les fonctions du §7 : optimisation du tick, +Significance, outils de mesure, instanciation. Candidats à examiner, au choix de l'utilisateur : +- gratuits : *Ultimate Optimization Toolkit* (IchiQ), *Optimization Tools plugin* (Creative Elicya) ; +- adaptés aux nombreux PNJ : *Significance Optimizer* (MateoPlugins), *Flex Significance* + (GameRec Studios), *Open World NPC Optimization* (Sergio Cavalli) ; +- gestion des assets : *Project Optimization Toolkit* (Leartes Tools). + +Règles : +- l'ajout d'un produit à la bibliothèque Fab se fait par l'utilisateur, avec son compte ; +- tout plugin tiers est évalué **dans une copie du projet**, jamais livré sans lot dédié ; +- il ne doit jamais devenir une dépendance du build sans accord. + +Recommandation : commencer par les fonctions du moteur (§7), qui couvrent les besoins identifiés sans +dépendance nouvelle. diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index a05b1de..3d3077b 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -903,3 +903,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG | Date | Lot | Fichiers | Raison | Retour arrière | |---|---|---|---|---|---| | CHG-058 | 2026-10-02 | Doc | nouveaux `07_Architecture_5.5.md`, `08_Guide_Developpeur.md`, `09_Commandes_Utiles.md`, `10_Protocole_Operations.md` ; mis à jour `README.md` (migration et racine), `01`, `03`, `04` (bilan §5), `05` (CX-C14 à C18, statuts), `06` (résultat V1), registre (ERR-020) | finalisation de la documentation de migration : architecture finale, guide développeur, commandes, protocole et leçons | `git revert` du commit | +| CHG-059 | 2026-10-02 | O0 | nouveaux `Tools/AuditAssets.py`, `Docs/Migration/11_Audit_Optimisation.md` ; lanceur `LifePlay_profil_O0.bat` (build L7cdj, hors dépôt) | audit d'optimisation en lecture seule : inventaire des assets, build livré, leviers et lots proposés ; aucune modification du projet | `git revert` du commit | diff --git a/Docs/Migration/README.md b/Docs/Migration/README.md index d2f2e23..9de948d 100644 --- a/Docs/Migration/README.md +++ b/Docs/Migration/README.md @@ -21,6 +21,7 @@ La migration a été reprise le 2026-09-29, après l'échec d'un premier portage | [08_Guide_Developpeur.md](08_Guide_Developpeur.md) | développeurs | environnement, compilation, packaging, commandlets, écrire un correctif, diagnostiquer, publier | | [09_Commandes_Utiles.md](09_Commandes_Utiles.md) | développeurs | commandes prêtes à copier, options de diagnostic du jeu | | [10_Protocole_Operations.md](10_Protocole_Operations.md) | **obligatoire avant toute modification** | déroulé d'un lot, règles de diagnostic, leçons de la campagne, tests avec l'utilisateur | +| [11_Audit_Optimisation.md](11_Audit_Optimisation.md) | pilotage | chantier d'optimisation UE5 : audit (lot O0), goulots, leviers, lots O1 à O8, Fab | | [Journal_Modifications.md](Journal_Modifications.md) | traçabilité | `CHG-001` à `CHG-057`, chacune avec son retour arrière | | [Registre_Erreurs.md](Registre_Erreurs.md) | traçabilité | `ERR-001` à `ERR-037`, statut et lot | | `correctifs/` | relecture | écarts de code 4.27 → 5.5 en patchs (`P01` à `P06`) | diff --git a/Tools/AuditAssets.py b/Tools/AuditAssets.py new file mode 100644 index 0000000..a928285 --- /dev/null +++ b/Tools/AuditAssets.py @@ -0,0 +1,69 @@ +# Audit d'optimisation (lot O0) : inventaire de /Game par le registre d'assets, sans charger les assets. +# Lecture seule. Pour chaque asset : paquet, classe, taille du fichier, tags du registre (dimensions, +# compression, triangles, LOD, Nanite...), nombre de paquets qui le referencent (durs et souples). +# +# Usage : +# UnrealEditor-Cmd.exe -run=pythonscript -script="/Tools/AuditAssets.py" -unattended -nopause -nosplash +# Sortie : Saved/Audit/assets.jsonl (une ligne JSON par asset) + +import json +import os +import unreal + +out_dir = os.path.join(unreal.Paths.project_saved_dir(), "Audit") +os.makedirs(out_dir, exist_ok=True) +out_path = os.path.join(out_dir, "assets.jsonl") + +registry = unreal.AssetRegistryHelpers.get_asset_registry() +registry.search_all_assets(True) +assets = registry.get_assets_by_path("/Game", recursive=True) + +hard = unreal.AssetRegistryDependencyOptions(include_soft_package_references=False, include_hard_package_references=True, + include_searchable_names=False, include_soft_management_references=False, + include_hard_management_references=False) +soft = unreal.AssetRegistryDependencyOptions(include_soft_package_references=True, include_hard_package_references=False, + include_searchable_names=False, include_soft_management_references=False, + include_hard_management_references=False) + +content_dir = unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_content_dir()) + +def package_file(package): + rel = str(package)[len("/Game/"):] + for ext in (".uasset", ".umap"): + p = os.path.join(content_dir, rel + ext) + if os.path.exists(p): + return p + return None + +count = 0 +with open(out_path, "w", encoding="utf-8") as f: + for a in assets: + package = a.package_name + path = package_file(package) + size = os.path.getsize(path) if path else -1 + if path: + ubulk = path[:-len(".uasset")] + ".ubulk" if path.endswith(".uasset") else None + if ubulk and os.path.exists(ubulk): + size += os.path.getsize(ubulk) + tags = {} + for key in ("Dimensions", "Format", "CompressionSettings", "LODGroup", "SRGB", "NeverStream", "MipGenSettings", + "Triangles", "Vertices", "LODs", "Materials", "NaniteEnabled", "ApproxSize", "Sections", + "MorphTargets", "Bones", "Skeleton", "BlendMode", "ShadingModel", "TwoSided", "Duration", + "NumFrames", "SequenceLength", "ParentClass", "NativeParentClass"): + v = a.get_tag_value(key) + if v is not None and v != "" and v != "None": + tags[key] = str(v) + refs_hard = registry.get_referencers(package, hard) or [] + refs_soft = registry.get_referencers(package, soft) or [] + rec = { + "package": str(package), + "class": str(a.asset_class_path.asset_name), + "size": size, + "tags": tags, + "refs_hard": len([r for r in refs_hard if str(r) != str(package)]), + "refs_soft": len([r for r in refs_soft if str(r) != str(package)]), + } + f.write(json.dumps(rec, ensure_ascii=False) + "\n") + count += 1 + +unreal.log_warning("AuditAssets : {} assets -> {}".format(count, out_path))