VQ : qualite video (sous-systeme LPVideoQuality, onglet Reglages, DefaultScalability), lots VQ0 a VQ5 et correctifs (CHG-237 a 242)
LifePlay.uproject : SupportedTargetPlatforms Win64 sur LPAnimCore, origine non revendiquee, range ici. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bfc6c3a1aa
commit
0f1a61c9fe
22 changed files with 2614 additions and 14 deletions
127
Config/DefaultScalability.ini
Normal file
127
Config/DefaultScalability.ini
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
; =============================================================================
|
||||
; Lot VQ1 (VQ1-T2) - Scalabilite projet, niveaux 0 (Low) a 3 (Epic).
|
||||
; Specification : Docs/Migration/etat/VQ/VQ1/VQ1-SPEC.md
|
||||
;
|
||||
; Groupes couverts UNIQUEMENT : ShadowQuality, TextureQuality, EffectsQuality,
|
||||
; AntiAliasingQuality.
|
||||
; Interdit ici : GI dynamique, ombres virtuelles (VSM), methode d'AA, methode de reflets
|
||||
; (verrouilles par DefaultEngine.ini, lots L7c / L6a ; ne rien ajouter a ce sujet).
|
||||
; Les cles non listees conservent les valeurs de Engine/Config/BaseScalability.ini.
|
||||
; Niveau Cinematic (@Cine) non touche.
|
||||
; =============================================================================
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; sg.ShadowQuality
|
||||
; ---------------------------------------------------------------------------
|
||||
[ShadowQuality@0]
|
||||
r.Shadow.CSM.MaxCascades=1
|
||||
r.Shadow.MaxResolution=512
|
||||
r.Shadow.MaxCSMResolution=512
|
||||
r.Shadow.RadiusThreshold=0.06
|
||||
r.Shadow.DistanceScale=0.6
|
||||
r.Shadow.CSM.TransitionScale=0.0
|
||||
r.Shadow.PreShadowResolutionFactor=0.5
|
||||
|
||||
[ShadowQuality@1]
|
||||
r.Shadow.CSM.MaxCascades=2
|
||||
r.Shadow.MaxResolution=1024
|
||||
r.Shadow.MaxCSMResolution=1024
|
||||
r.Shadow.RadiusThreshold=0.05
|
||||
r.Shadow.DistanceScale=0.7
|
||||
r.Shadow.CSM.TransitionScale=0.25
|
||||
r.Shadow.PreShadowResolutionFactor=0.5
|
||||
|
||||
[ShadowQuality@2]
|
||||
r.Shadow.CSM.MaxCascades=3
|
||||
r.Shadow.MaxResolution=2048
|
||||
r.Shadow.MaxCSMResolution=2048
|
||||
r.Shadow.RadiusThreshold=0.04
|
||||
r.Shadow.DistanceScale=0.85
|
||||
r.Shadow.CSM.TransitionScale=0.6
|
||||
r.Shadow.PreShadowResolutionFactor=0.75
|
||||
|
||||
[ShadowQuality@3]
|
||||
r.Shadow.CSM.MaxCascades=4
|
||||
r.Shadow.MaxResolution=2048
|
||||
r.Shadow.MaxCSMResolution=2048
|
||||
r.Shadow.RadiusThreshold=0.03
|
||||
r.Shadow.DistanceScale=1.0
|
||||
r.Shadow.CSM.TransitionScale=1.0
|
||||
r.Shadow.PreShadowResolutionFactor=1.0
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; sg.TextureQuality
|
||||
; ---------------------------------------------------------------------------
|
||||
[TextureQuality@0]
|
||||
r.Streaming.MipBias=2.5
|
||||
r.MaxAnisotropy=0
|
||||
r.Streaming.PoolSize=200
|
||||
r.Streaming.MaxEffectiveScreenSize=0
|
||||
|
||||
[TextureQuality@1]
|
||||
r.Streaming.MipBias=1
|
||||
r.MaxAnisotropy=2
|
||||
r.Streaming.PoolSize=400
|
||||
r.Streaming.MaxEffectiveScreenSize=0
|
||||
|
||||
[TextureQuality@2]
|
||||
r.Streaming.MipBias=0
|
||||
r.MaxAnisotropy=4
|
||||
r.Streaming.PoolSize=700
|
||||
r.Streaming.MaxEffectiveScreenSize=0
|
||||
|
||||
[TextureQuality@3]
|
||||
r.Streaming.MipBias=0
|
||||
r.MaxAnisotropy=8
|
||||
r.Streaming.PoolSize=1000
|
||||
r.Streaming.MaxEffectiveScreenSize=0
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; sg.EffectsQuality
|
||||
; ---------------------------------------------------------------------------
|
||||
[EffectsQuality@0]
|
||||
r.TranslucencyLightingVolumeDim=24
|
||||
r.RefractionQuality=0
|
||||
r.SSS.Scale=0.25
|
||||
r.SSS.SampleSet=0
|
||||
r.DetailMode=0
|
||||
fx.Niagara.QualityLevel=0
|
||||
|
||||
[EffectsQuality@1]
|
||||
r.TranslucencyLightingVolumeDim=32
|
||||
r.RefractionQuality=2
|
||||
r.SSS.Scale=0.5
|
||||
r.SSS.SampleSet=1
|
||||
r.DetailMode=1
|
||||
fx.Niagara.QualityLevel=1
|
||||
|
||||
[EffectsQuality@2]
|
||||
r.TranslucencyLightingVolumeDim=48
|
||||
r.RefractionQuality=2
|
||||
r.SSS.Scale=0.75
|
||||
r.SSS.SampleSet=2
|
||||
r.DetailMode=2
|
||||
fx.Niagara.QualityLevel=2
|
||||
|
||||
[EffectsQuality@3]
|
||||
r.TranslucencyLightingVolumeDim=64
|
||||
r.RefractionQuality=3
|
||||
r.SSS.Scale=1.0
|
||||
r.SSS.SampleSet=2
|
||||
r.DetailMode=2
|
||||
fx.Niagara.QualityLevel=3
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; sg.AntiAliasingQuality (qualite uniquement ; la methode d'AA est fixee par DefaultEngine.ini)
|
||||
; ---------------------------------------------------------------------------
|
||||
[AntiAliasingQuality@0]
|
||||
r.PostProcessAAQuality=0
|
||||
|
||||
[AntiAliasingQuality@1]
|
||||
r.PostProcessAAQuality=2
|
||||
|
||||
[AntiAliasingQuality@2]
|
||||
r.PostProcessAAQuality=4
|
||||
|
||||
[AntiAliasingQuality@3]
|
||||
r.PostProcessAAQuality=4
|
||||
137
Docs/Migration/23_Video_Quality.md
Normal file
137
Docs/Migration/23_Video_Quality.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# 23 — Qualité vidéo et réglages graphiques (chantier VQ)
|
||||
|
||||
> **V1 close — VQ4, 2026-10-07** (voir §8 Acceptation). Ébauche initiale VQ0-T3 : documentation de cadrage pour la V1 du chantier **VQ** (rationalisation de l'onglet **Video**).
|
||||
>
|
||||
> Cartographie validée : `etat/VQ/VQ0/VQ0-CART.md` (VQ0-T1), preuve : `etat/VQ/VQ0/VQ0-VAL.md` (VQ0-T2, PASS).
|
||||
|
||||
## 1. Périmètre V1
|
||||
|
||||
La **V1 du chantier VQ** vise un socle C++ **`ULPVideoQualitySubsystem`** (nom provisoire) qui :
|
||||
|
||||
1. **Injecte** un bloc de réglages `LPVideo*` dans `MainMenu.SettingsScrollBox`, **avant** les cinq lignes `LPColor*` (`LPColorGradeSubsystem`).
|
||||
2. **Ne remplace pas** en V1 l'intégralité du menu Video Blueprint : les lignes LifePlay spécifiques (Toon, censure, morphs, jiggle, DoF, etc.) restent telles qu'elles sont classées par `LPSettingsSubsystem`.
|
||||
3. **Prépare la sync** avec `UGameUserSettings` et les groupes Scalability Unreal pour les curseurs déjà présents en Blueprint (`ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality`, `ScreenPercentage`) — soit en les **pilotant** depuis C++, soit en **masquant** le widget BP et en exposant un contrôle unique `LPVideo*`.
|
||||
4. **Corrige** le faux-positif `NewAnimationsOnly` (classé Video à cause du mot « quality » dans l'infobulle) — lot connexe sur `CategoryOf` ou renommage/tooltip BP ; peut être livré dans le même lot ou immédiatement avant.
|
||||
|
||||
Hors périmètre V1 : résolution / mode fenêtré avancé au-delà de `FullscreenButton`, presets « Ultra / Cinematic », motion blur, DLSS/FSR, profils par machine, migration de `GameSettings.sav`.
|
||||
|
||||
## 2. Synchronisation (sync)
|
||||
|
||||
| Couche | Rôle V1 | Fichier / API |
|
||||
|---|---|---|
|
||||
| Blueprint existant | Curseurs Scalability et cases à cocher ; écriture historique via le flux Blueprint / `GameSettings` | `Content/UI/MainMenu.uasset` |
|
||||
| `LPSettingsSubsystem` | Classement onglet Video (`CategoryOf`, `ApplyRows`) ; ne modifie pas les valeurs graphiques | `Source/LifePlay/LPSettingsSubsystem.cpp` |
|
||||
| Futur `LPVideo*` | Source de vérité **affichée** pour les leviers retenus ; applique `Scalability::` / `GEngine->Exec` / `GameUserSettings` ; relit à l'ouverture du menu | À créer |
|
||||
| `LPColorGradeSubsystem` | Color grading post-process ; **indépendant** (`LPColorGrade.ini`) | `Source/LifePlay/LPColorGradeSubsystem.cpp` |
|
||||
|
||||
Règle V1 : **une seule commande visible par CVar Scalability** — si un `LPVideo*` expose `Shadow Quality`, le `SettingSlider` BP `ShadowQuality` doit être `Collapsed` (ou synchronisé bidirectionnellement sans boucle ; préférer masquage + sync à l'injection).
|
||||
|
||||
Ordre d'initialisation recommandé (tick ou première ouverture des réglages) :
|
||||
|
||||
1. `LPSettingsSubsystem::EnsureTabs` — structure d'onglets prête.
|
||||
2. `LPVideoQualitySubsystem::EnsureControls` — insertion **avant** le premier enfant dont le nom commence par `LPColor`.
|
||||
3. `LPColorGradeSubsystem::EnsureControls` — ajout en queue si absent (comportement actuel).
|
||||
|
||||
### 2.1 État après VQ2 / VQ3 (2026-10-07)
|
||||
|
||||
`ULPVideoQualitySubsystem` (`Source/LifePlay/LPVideoQualitySubsystem.*`) est en place ; la ligne « Futur `LPVideo*` » du tableau ci-dessus est donc réalisée.
|
||||
|
||||
| Sujet | Comportement |
|
||||
|---|---|
|
||||
| Lignes injectées | `LPVideoOverall`, `LPVideoShadows`, `LPVideoTextures`, `LPVideoEffects`, `LPVideoAA`, `LPVideoRecommended`, insérées avant le premier `LPColor*` |
|
||||
| Blueprint masqués | `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` (`TabHidden` dans `CategoryOf`) |
|
||||
| Application | `Scalability::SetQualityLevels` sur 4 groupes seulement (`sg.ShadowQuality`, `sg.TextureQuality`, `sg.EffectsQuality`, `sg.AntiAliasingQuality`), relecture et repli console |
|
||||
| Overall | Curseur Overall → `FromOverall` : les 4 curseurs s'alignent. Un curseur individuel → `Normalize()` : Overall = niveau commun, sinon `Custom` (-1) |
|
||||
| Persistance | `Saved/Config/Windows/LPVideoQuality.ini` (`Version`, `Overall`, `Shadows`, `Textures`, `Effects`, `AA`), relu à l'initialisation ; sans INI ni action du joueur, l'état moteur est conservé |
|
||||
| **Recommended (VQ3)** | Lit `DedicatedVideoMemory` via `RHIGetTextureMemoryStats` : < 4 Go → 0 Low ; < 6 → 1 Med ; < 10 → 2 High ; sinon 3 Epic. VRAM inconnue (0, -1, pas de RHI) → 2 High. Puis `SetLevels(FromOverall(niveau), true)` et journal `Video : Recommended = ... VRAM dediee detectee X Go` |
|
||||
|
||||
Limite connue : un GPU intégré annonce une VRAM dédiée faible (partage avec la RAM système) et obtient donc Low ou Med ; `SharedSystemMemory` n'est volontairement pas pris en compte.
|
||||
|
||||
Preuves : `etat/VQ/VQ2/VQ2-VAL.md`, `etat/VQ/VQ3/VQ3-IMPL-H.md`, `etat/VQ/VQ3/VQ3-VAL.md`.
|
||||
|
||||
## 3. Ancrage rendu L7c
|
||||
|
||||
Les valeurs de rendu **validées en jeu** (lot **L7c**, CHG-049) restent la baseline visuelle ; les réglages Video ne doivent pas les écraser implicitement :
|
||||
|
||||
| Clé | Valeur jouée | Fichier |
|
||||
|---|---|---|
|
||||
| `r.DynamicGlobalIlluminationMethod` | `0` | `Config/DefaultEngine.ini` |
|
||||
| `r.ReflectionMethod` | `2` (SSR) | idem |
|
||||
| `r.Shadow.Virtual.Enable` | `0` | idem |
|
||||
| `DefaultGraphicsRHI` | D3D12 | idem |
|
||||
|
||||
Référence : `07_Architecture_5.5.md`, `04_Cahier_Des_Charges.md` (L7c), `Journal_Modifications.md` (CHG-049). Toute évolution VQ post-V1 qui touche ces clés exige une campagne de validation jour/nuit comparable à L7c/L7d.
|
||||
|
||||
## 4. Injection UI : avant `LPColor*`
|
||||
|
||||
**Motif ergonomique** : affichage / scalabilité / effets LifePlay d'abord ; **étalonnage colorimétrique** (`LPColor*`) en fin d'onglet Video (cf. CART §7).
|
||||
|
||||
**Motif technique** : aujourd'hui `ULPColorGradeSubsystem::AddRow` appelle `Scroll->AddChild(Row)` — les lignes `LPColor*` sont toujours **dernières**. Le futur sous-système doit :
|
||||
|
||||
- repérer l'index du premier enfant `LPColor*` (ou `GetChildrenCount()` si absent) ;
|
||||
- utiliser `UScrollBox::InsertChildAt(Index, Row)` (ou équivalent) pour le bloc `LPVideo*` ;
|
||||
- laisser `LPColorGradeSubsystem` inchangé en V1 (append) pour minimiser le diff.
|
||||
|
||||
Widgets injectés : préfixe de nom **`LPVideo`** (classification Video garantie par le même mécanisme que `LPColor*`, ou règle dédiée dans `CategoryOf`).
|
||||
|
||||
## 5. Conflits Blueprint — note V1
|
||||
|
||||
| Conflit | Widgets / leviers | Traitement V1 proposé |
|
||||
|---|---|---|
|
||||
| **Ombres** | BP `ShadowQuality` (`sg.ShadowQuality`) vs futur `LPVideoShadow*` | Un seul contrôle visible ; masquer BP si C++ prend le relais |
|
||||
| **Post-process** | BP `PostProcessQuality` (`sg.PostProcessQuality`) vs preset global `LPVideo*` | Idem ; documenter que « PP Quality (FPS intensive) » reste le libellé utilisateur jusqu'à refonte textes |
|
||||
| **Anti-aliasing** | BP `PostProcessAAQuality` (`r.PostProcessAAQuality`) vs `LPVideoAA*` | Idem |
|
||||
| **Échelle interne** | BP `ScreenPercentage` vs `LPVideo*` render scale | Sync sur `r.ScreenPercentage` / `GameUserSettings` selon API retenue |
|
||||
| **Luminosité** | `LPColorBrightness` (`AutoExposureBias`) vs gamma écran | **Ne pas** fusionner en V1 ; distinct « exposition 3D » (Color) vs futur gamma (Video) |
|
||||
| **Faux-positif onglet** | `NewAnimationsOnly` | Corriger classification (General) — indépendant des CVars |
|
||||
|
||||
## 6. Prochaines fiches (hors VQ0)
|
||||
|
||||
| Fiche | Contenu attendu |
|
||||
|---|---|
|
||||
| VQ1-SPEC | API `LPVideo*`, liste des CVars, persistance (ini dédié vs `GameUserSettings`) |
|
||||
| VQ1-IMPL | Module C++, masquage BP, tests menu Video |
|
||||
| VQ1-VAL | PASS/FAIL en jeu + non-régression L7c |
|
||||
|
||||
## 7. Documents liés
|
||||
|
||||
- `etat/VQ/VQ0/VQ0-CART.md` — inventaire exhaustif onglet Video
|
||||
- `etat/VQ/VQ0/VQ0-VAL.md` — validation amont
|
||||
- `14_Interface_V1.4.md` — menu principal (contexte UI)
|
||||
- `10_Protocole_Operations.md` — déroulé de lot avant code
|
||||
- `etat/VQ/VQ1/VQ1-SPEC.md` §8, `etat/VQ/VQ2/VQ2-SPEC.md` §13 — critères d'acceptation d'origine
|
||||
- `etat/VQ/VQ4/VQ4-VAL.md` — validation finale V1 (PASS, BLOCKED-UI manuel)
|
||||
- `24_Eval_Agents_VideoQuality.md` + `etat/VQ/VQ5/VQ5-VAL.md` — évaluation des agents VQ5 (GO conditionnel ; écarts Epic de `DefaultScalability.ini`)
|
||||
|
||||
## 8. Acceptation V1 (clôture VQ4, 2026-10-07)
|
||||
|
||||
Statut : **V1 close, PASS** (`etat/VQ/VQ4/VQ4-VAL.md`). Les cases cochées sont prouvées par relecture du code, grep et démarrage `-nullrhi`. Les cases non cochées exigent le menu Settings ouvert en jeu (**BLOCKED-UI**), à faire à la main ; elles ne bloquent pas la clôture.
|
||||
|
||||
### 8.1 Configuration et non-régression L7c
|
||||
|
||||
- [x] `Config/DefaultScalability.ini` ne contient que `[ShadowQuality@0..3]`, `[TextureQuality@0..3]`, `[EffectsQuality@0..3]`, `[AntiAliasingQuality@0..3]` (16 sections).
|
||||
- [x] Aucune occurrence de `Virtual`, `Lumen`, `GlobalIllumination`, `AntiAliasingMethod`, `ReflectionMethod`, `SSR` dans `DefaultScalability.ini` (grep insensible à la casse, 0 hit).
|
||||
- [x] `Config/DefaultEngine.ini` inchangé : L7c intact (GI 0, SSR, VSM 0, TAA, D3D12).
|
||||
- [x] Aucune cvar L7c dans le code `LPVideoQualitySubsystem.*`.
|
||||
- [ ] Niveau 3 en jeu : GI dynamique, VSM et méthode d'AA inchangés (BLOCKED-UI).
|
||||
|
||||
### 8.2 Code et classement
|
||||
|
||||
- [x] `LPVideoQualitySubsystem.h/.cpp` compilent ; DLL `UnrealEditor-LifePlay.dll` postérieure aux sources.
|
||||
- [x] `SetLevels` : Overall → 4 niveaux (`FromOverall`) ; niveau individuel → `Normalize` (Overall ou `Custom`).
|
||||
- [x] `Recommended` : VRAM dédiée (`< 4` Low, `< 6` Med, `< 10` High, sinon Epic ; inconnue = High).
|
||||
- [x] `CategoryOf` : `LPVideo*` et `LPColor*` → Video ; `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` → `TabHidden`.
|
||||
- [x] `LPColorGradeSubsystem` non modifié.
|
||||
- [x] Démarrage `-nullrhi` : exit 0, lecture des niveaux depuis Scalability, aucun avertissement vidéo.
|
||||
|
||||
### 8.3 Menu Settings (test manuel)
|
||||
|
||||
- [ ] Six lignes `LPVideo*` avant `LPColor*` ; trois curseurs BP masqués après changement d'onglet (BLOCKED-UI).
|
||||
- [ ] Overall 0→3 : les 4 curseurs suivent ; un curseur individuel → `Custom` (BLOCKED-UI).
|
||||
- [ ] `Recommended` : niveau conforme au GPU, journal VRAM (BLOCKED-UI).
|
||||
- [ ] Relance : niveaux relus depuis `Saved/Config/Windows/LPVideoQuality.ini` (BLOCKED-UI).
|
||||
|
||||
### 8.4 Limites connues V1
|
||||
|
||||
- GPU intégré : VRAM dédiée faible, donc Low ou Med ; `SharedSystemMemory` volontairement ignorée.
|
||||
- `PostProcessQuality` n'est plus pilotable par le joueur (masqué, hors des 4 groupes) ; `ScreenPercentage` reste en Blueprint.
|
||||
92
Docs/Migration/24_Eval_Agents_VideoQuality.md
Normal file
92
Docs/Migration/24_Eval_Agents_VideoQuality.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# 24 — Évaluation des agents : chantier Video Quality (VQ0–VQ4)
|
||||
|
||||
> **VQ5-T1, 2026-10-07.** Évaluation du déroulé multi-agents du chantier VQ (doc `23_Video_Quality.md`).
|
||||
> Annexe de preuve : `etat/VQ/VQ5/VQ5-VAL.md` (VQ5-T2). Journal : CHG-237 à CHG-240.
|
||||
> Modèle : `claude-opus-5-5-medium` (M4).
|
||||
|
||||
## 1. Résumé exécutif
|
||||
|
||||
Le chantier VQ a livré la V1 prévue en cinq lots : un `DefaultScalability.ini` à 4 groupes, le sous-système `ULPVideoQualitySubsystem` (6 lignes `LPVideo*` avant `LPColor*`, 3 curseurs Blueprint masqués), un bouton Recommended basé sur la VRAM, et une clôture documentaire. Tout compile, le démarrage `-nullrhi` est propre, aucun verrou L7c n'est touché, et chaque lot a un verdict PASS.
|
||||
|
||||
Deux limites pèsent sur ce bilan :
|
||||
|
||||
1. **Aucun clic n'a été exécuté.** Les chemins Overall, Custom et Recommended sont prouvés par relecture du code, pas en jeu (BLOCKED-UI depuis VQ2).
|
||||
2. **Un point de passage entre agents a été perdu.** VQ1-IMPL demandait de comparer les valeurs à `BaseScalability.ini` du moteur dans une fiche VQ1-VAL qui n'a jamais été produite. VQ5 a fait la comparaison : au niveau Epic (niveau actif au démarrage), le projet réduit les cascades d'ombre (10 → 4), relève le seuil d'ombre et le `DetailMode`, et dépasse le palier moteur pour `r.RefractionQuality` (2 → 3). Le rendu change donc pour tous les joueurs, même sans ouvrir l'onglet Video (`VQ5-VAL.md` §4).
|
||||
|
||||
**Décision : GO conditionnel** pour réutiliser ce schéma d'agents (§6).
|
||||
|
||||
## 2. Lots VQ0–VQ4
|
||||
|
||||
| Lot | Fiches tenues | Coupé / non livré | Rollback | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| **VQ0** | T1 CART (inventaire Video, 18 lignes + 5 `LPColor*`), T2 VAL, T3 DOC (doc 23) | — (réserve mineure : décompte « 4 » au lieu de 5 en CART §8) | CHG-237 : supprimer doc 23 et `VQ0-VAL.md` | **PASS** |
|
||||
| **VQ1** | T1 SPEC, T2 IMPL (`Config/DefaultScalability.ini`, 16 sections), T3 CART (audit grep des interdits L7c, 0 hit) | **VQ1-VAL non produit** : comparaison à `BaseScalability.ini` et lecture des cvars en jeu jamais faites ; **pas de CHG** | non journalisé (supprimer `DefaultScalability.ini`) | **PASS** sur le contrat L7c ; **réserve** sur les valeurs |
|
||||
| **VQ2** | T1 SPEC, T2 + T3 fusionnés dans IMPL-H (sous-système + `CategoryOf`/`ApplyRows`), T4 VAL | delegate `OnControlsInjected` (Q2 : inutile, le ticker de `LPSettingsSubsystem` suffit) ; `PostProcessQuality` figé (Q1, choix assumé) ; correction du faux positif `NewAnimationsOnly` (doc 23 §1.4) **non livrée** ; **pas de CHG** | non journalisé (supprimer `LPVideoQualitySubsystem.*`, revenir sur `LPSettingsSubsystem.cpp`) | **PASS** + BLOCKED-UI |
|
||||
| **VQ3** | T1 IMPL-H (VRAM → niveau), T2 VAL | `SharedSystemMemory` ignorée (GPU intégré → Low/Med, assumé) | CHG-238 : restaurer le stub, retirer `RHI` de `LifePlay.Build.cs` | **PASS** + BLOCKED-UI |
|
||||
| **VQ4** | T1 VAL finale, §8 Acceptation du doc 23 | rejeu des cases d'acceptation in-game (BLOCKED-UI) | CHG-239 : supprimer `VQ4-VAL.md`, retirer §8 | **PASS** + BLOCKED-UI |
|
||||
|
||||
Aucun rollback n'a été exécuté. Rien n'est commité : tout le chantier est encore non suivi ou modifié dans l'arbre de travail.
|
||||
|
||||
## 3. Qualité des consignes et des passages entre agents
|
||||
|
||||
**Ce qui a bien marché**
|
||||
|
||||
- **Les SPEC sont exécutables.** VQ2-SPEC fixe les types, l'ordre strict de `SetLevels`, les noms de widgets, l'algorithme d'insertion et son repli, les risques (R1–R7) et les questions ouvertes. L'implémentation a compilé au premier passage avec des écarts minimes et documentés (`bConfigured`, signature d'`AddRow`).
|
||||
- **Les interdits sont explicites.** La liste L7c (GI, VSM, méthode d'AA, reflets) est répétée dans chaque SPEC et rejouée par grep en VQ1-CART et VQ4-VAL ; elle n'a jamais été violée.
|
||||
- **Les écarts sont signalés.** IMPL-H liste ses écarts ; les VAL marquent honnêtement BLOCKED-UI au lieu de cocher les cases de jeu.
|
||||
- **Les preuves sont traçables.** Chaque VAL donne la commande, l'exit code, le journal (`Saved/Logs/VQ{2,3,4}VAL.log`) et les lignes de code.
|
||||
|
||||
**Ce qui a manqué**
|
||||
|
||||
- **Les points à confirmer ne sont pas suivis.** La liste « Points à confirmer en validation » de VQ1-IMPL §4 n'a pas été reprise : VQ1-T3 a reçu une consigne d'audit limitée au grep des interdits, VQ4 a rejoué les cases de la SPEC sans relire IMPL. C'est la cause directe de l'écart Epic non détecté.
|
||||
- **La SPEC VQ1 n'a pas été vérifiée.** Elle annonçait des paliers « au plus égaux à Epic moteur » sans avoir lu le fichier moteur (IMPL le reconnaît). Une hypothèse non vérifiée a ensuite été recopiée comme acquise (« sans INI, aucun changement », VQ2-SPEC §6.2, doc 23 §2.1).
|
||||
- **Le journal est incomplet.** VQ1 et VQ2, les deux seuls lots qui créent des fichiers de code ou de configuration, n'ont pas de CHG ; leur rollback n'est donc écrit nulle part.
|
||||
- **Le modèle réellement utilisé n'est pas toujours noté.** VQ0-CART, VQ1-SPEC/IMPL et VQ2-SPEC ne notent que le modèle attendu, ou rien.
|
||||
- **Le doc 23 contient une section périmée.** §6 « Prochaines fiches » décrit encore un VQ1 qui ne correspond pas au découpage réel.
|
||||
|
||||
## 4. Adéquation modèle ↔ complexité
|
||||
|
||||
| Fiche | Complexité | Modèle prévu | Modèle réel | Appréciation |
|
||||
|---|---|---|---|---|
|
||||
| VQ0-T1 CART | moyenne (lecture de code + `.uasset`) | non noté | non noté | livrable complet ; modèle non traçable |
|
||||
| VQ0-T2 VAL | faible (recoupement) | composer-2.5-fast (M0) | idem | adapté |
|
||||
| VQ1-T1/T2 SPEC + IMPL | moyenne (valeurs moteur) | claude-sonnet-5-5-high (M2) | non noté | rédaction solide, mais aucune lecture de `BaseScalability.ini` (contexte d'installation absent) |
|
||||
| VQ1-T3 CART | faible (grep) | gemini-3.8-flash-high (M1) | idem | adapté au contrat, mais contrat trop étroit (§3) |
|
||||
| VQ2-T1 SPEC | élevée (UMG, Scalability, priorités cvar) | claude-sonnet-5-5-high (M2) | non noté | très bonne qualité |
|
||||
| **VQ2-T2 IMPL-H** | **élevée** | **claude-fable-5-1-thinking-high (bloqué par la policy)** | **claude-sonnet-5-5-high (repli)** | le repli a suffi : compile au premier passage, écarts justifiés. La SPEC détaillée a compensé l'écart de modèle |
|
||||
| VQ2-T4 VAL | moyenne | sonnet (M2) | idem | adapté |
|
||||
| VQ3-T1/T2 | faible (seuils VRAM, API RHI) | sonnet (repli M3) | idem | surdimensionné ; un M1 aurait suffi, mais la vérification de l'API dans les sources UE 5.5 est de bonne qualité |
|
||||
| VQ4-T1 VAL | faible (rejeu) | sonnet | idem | adapté ; aurait dû relire les IMPL, pas seulement les SPEC |
|
||||
| VQ5-T1/T2 EVAL | moyenne (synthèse critique) | claude-opus-5-5-medium (M4) | idem | adapté |
|
||||
|
||||
Conclusion : le blocage de Fable n'a pas dégradé le résultat, parce que la SPEC VQ2 portait l'essentiel de la complexité. Le vrai risque ne venait pas d'un modèle trop faible, mais d'une consigne trop étroite (VQ1-T3) et d'une fiche VAL absente.
|
||||
|
||||
## 5. Exigence de sync respectée ?
|
||||
|
||||
**Oui en code, non prouvée en jeu (BLOCKED-UI manuel).**
|
||||
|
||||
| Règle (doc 23 §2) | État |
|
||||
|---|---|
|
||||
| Une seule commande visible par cvar Scalability | **oui** : `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` → `TabHidden`, maintenus repliés par `ApplyRows` |
|
||||
| Overall ↔ 4 curseurs sans boucle | **oui** : point d'entrée unique `SetLevels` ; `FromOverall` / `Normalize` ; garde `bRefreshing` (`VQ5-VAL.md` §3) |
|
||||
| Ré-application après le Blueprint | **oui** : nouveau monde de jeu (`Tick`) et chaque injection du menu (`EnsureControls`) |
|
||||
| Persistance | **oui** (code) : `LPVideoQuality.ini` relu à `Initialize` ; `GameUserSettings.ini` volontairement non écrit |
|
||||
| Preuve en jeu | **BLOCKED-UI** : 5 tests manuels listés en VQ4-VAL §4, jamais exécutés |
|
||||
|
||||
Limites acceptées : un `sg.*` tapé en console n'est pas repris dans les curseurs une fois le joueur configuré ; `PostProcessQuality` n'est plus pilotable ; chaque cran de curseur réécrit l'INI.
|
||||
|
||||
## 6. Go / no-go réutilisation
|
||||
|
||||
**GO conditionnel.** Le schéma SPEC → IMPL → VAL avec rôles séparés, interdits répétés et verdicts honnêtes est réutilisable pour un chantier C++/config de taille comparable. Deux conditions : la recommandation 1 doit être appliquée avant de déclarer VQ « livré joueur », et les recommandations 2 et 3 doivent être intégrées au gabarit des prochains chantiers.
|
||||
|
||||
### Recommandations
|
||||
|
||||
1. **Tracer chaque « point à confirmer » d'une IMPL jusqu'à une case de VAL, et ouvrir un lot VQ1b.** Chaque VAL doit relire la ou les IMPL amont, pas seulement la SPEC, et reporter chaque point : PASS, BLOCKED ou reporté avec un identifiant. Lot immédiat VQ1b : aligner `[*Quality@3]` (et `@2` si voulu) sur `BaseScalability.ini` (au minimum `r.Shadow.CSM.MaxCascades`, `r.Shadow.RadiusThreshold`, `r.DetailMode`, `r.RefractionQuality`), ou valider explicitement l'écart par une comparaison visuelle jour/nuit type L7c. Corriger ensuite la phrase « sans INI, aucun changement » dans le doc 23.
|
||||
2. **Rendre le BLOCKED-UI automatisable.** Ajouter des commandes console de debug (par exemple `LP.Video.Set <groupe> <niveau>`, `LP.Video.Overall <n>`, `LP.Video.Recommended`, `LP.Video.Dump`) qui appellent `SetLevels` / `OnRecommendedClicked` et affichent `sg.*` et les cvars clés. Le smoke `-nullrhi -ExecCmds` couvrira alors Overall, Custom et la persistance, et un lancement D3D12 court couvrira Recommended avec une vraie VRAM. Il ne restera en manuel que le rendu visuel de l'onglet.
|
||||
3. **Discipline de clôture par lot.** Un CHG par lot qui touche du code ou de la configuration (ici, VQ1 et VQ2 manquent) avec son rollback ; modèle prévu et réel dans l'en-tête de chaque fiche ; contrôle de disponibilité du modèle au dispatch (l'indisponibilité de Fable n'a été vue qu'à l'exécution) ; commit et tag du chantier en fin de lot (VQ est entièrement hors commit).
|
||||
|
||||
## 7. Annexes
|
||||
|
||||
- `etat/VQ/VQ5/VQ5-VAL.md` — chemins de code Overall / Custom / Recommended (PASS code), preuve de démarrage `-nullrhi` relue, écarts `DefaultScalability.ini` ↔ `BaseScalability.ini`.
|
||||
- `etat/VQ/VQ0` … `VQ4` — fiches CART, SPEC, IMPL et VAL évaluées.
|
||||
- `23_Video_Quality.md` — référence fonctionnelle du chantier.
|
||||
|
|
@ -1131,6 +1131,12 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol
|
|||
| CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul |
|
||||
| CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` |
|
||||
| CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` |
|
||||
| CHG-242 | 2026-10-08 | VQ-fix | `Source/LifePlay/LPSettingsSubsystem.cpp`, `LPSettingsSubsystem.h` | contraste UI Settings : `HardenSettingsControls` force un style Slate sombre/lisible sur toutes les cases et SpinBox (valeurs pâles sur fond flou) ; ticker Settings à 0 pour que `ApplyRows` gagne face au BP. Compile `LifePlayEditor` PASS | — → modifiés | retirer `HardenSettingsControls` / remettre ticker 0.5 |
|
||||
| CHG-241 | 2026-10-08 | VQ-fix | `Source/LifePlay/LPSettingsSubsystem.cpp`, `Source/LifePlay/LPVideoQualitySubsystem.cpp` | correctifs post-VQ : (1) `CategoryOf` ignore les infobulles (faux positifs `screen`/`quality` → Sex Description, Map Marker, NewAnimationsOnly hors Video) + classements par nom UMG (`ShowFPS`, etc.) ; (2) `MainMenu.GI` rattaché sur l'écran titre (spinbox DoF / cases) ; (3) `RepairShowFPS` (style + enabled) ; (4) `ApplyRows` à chaque tick ouvert ; (5) `ApplyToScalability` n'écrit plus que les 4 `sg.*` en `SetByConsole` (fin du spam `SetByScalability ignored` sur ScreenPercentage/PoolSize/SSS). Live Coding PASS | — → modifiés | revenir sur les deux `.cpp` |
|
||||
| CHG-240 | 2026-10-07 | VQ5 | `Docs/Migration/24_Eval_Agents_VideoQuality.md` (nouveau), `Docs/Migration/etat/VQ/VQ5/VQ5-VAL.md` (nouveau) | clôture VQ5 : évaluation des agents du chantier VQ (lots VQ0–VQ4 tenus/coupés/rollback, qualité des consignes, modèle ↔ complexité dont Fable bloqué → repli Sonnet en VQ2-T2, sync PASS en code et BLOCKED-UI en jeu). Décision **GO conditionnel** avec 3 recommandations. Annexe VQ5-VAL : chemins Overall / Custom / Recommended PASS (code), smoke `-nullrhi` de VQ4 relu (pas de nouveau run : aucun code changé, chemins non atteignables par console). Constat : `DefaultScalability.ini` s'écarte de `BaseScalability.ini` au niveau Epic (cascades 10 → 4, `RadiusThreshold`, `DetailMode`, `RefractionQuality` 2 → 3) ; VQ1-VAL jamais produit ; VQ1 et VQ2 sans CHG. Documentation seule, aucun code | — → créés | supprimer ces deux fichiers |
|
||||
| CHG-239 | 2026-10-07 | VQ4 | `Docs/Migration/23_Video_Quality.md` (§ 8 Acceptation V1), `Docs/Migration/etat/VQ/VQ4/VQ4-VAL.md` (nouveau) | clôture V1 du chantier VQ : validation finale PASS (`SetLevels` sync, Recommended VRAM, `CategoryOf` `LPVideo*` + `TabHidden`, `LPColor*` toujours Video, `DefaultScalability.ini` sans cvar L7c : grep 0 hit, `DefaultEngine.ini` inchangé, DLL à jour, démarrage `-nullrhi` exit 0). Critères d'acceptation cochés dans le doc 23 ; test manuel Settings resté BLOCKED-UI. Documentation seule, aucun code | - créés / modifiés | supprimer `VQ4-VAL.md` et retirer le § 8 du doc 23 |
|
||||
| CHG-238 | 2026-10-07 | VQ3 | `Source/LifePlay/LPVideoQualitySubsystem.cpp`, `Source/LifePlay/LifePlay.Build.cs` (module `RHI`), `Docs/Migration/23_Video_Quality.md` (§ 2 Sync), `Docs/Migration/etat/VQ/VQ3/VQ3-IMPL-H.md`, `VQ3-VAL.md` (nouveaux) | lot VQ3 : le bouton `LPVideoRecommended` n'applique plus un niveau fixe mais lit la VRAM dédiée (`RHIGetTextureMemoryStats`, `DedicatedVideoMemory`) : < 4 Go Low, < 6 Med, < 10 High, sinon Epic ; VRAM inconnue (0 / -1 / `-nullrhi`) = High. Application par `SetLevels(FromOverall(niveau), true)` (alignement des 4 curseurs, `Save`, `RefreshWidgets`). Compile `LifePlayEditor` OK, démarrage `-nullrhi` OK ; test manuel Settings BLOCKED-UI | — → modifiés | restaurer le stub (niveau 2 fixe) dans `OnRecommendedClicked` et retirer `RHI` de `LifePlay.Build.cs` |
|
||||
| CHG-237 | 2026-10-07 | VQ0 | `Docs/Migration/23_Video_Quality.md` (nouveau, ébauche), `Docs/Migration/etat/VQ/VQ0/VQ0-VAL.md` (nouveau) | lot VQ0 : validation A-VAL de la cartographie Video (`VQ0-CART`, PASS) ; ébauche de référence qualité vidéo (périmètre V1, sync GameUserSettings/Scalability, ancrage L7c, injection `LPVideo*` avant `LPColor*`, conflits BP Shadow/PP/AA). Documentation seule, aucun code | — → créés | supprimer ces fichiers |
|
||||
| CHG-236 | 2026-10-07 | DC3 | jeu : retrait (57 fichiers suivis) de `Tools/BuildMod.ps1`, `NewMod.ps1`, `EditMod.ps1`, `ModSource.ps1`, `HairPipeline.ps1`, `HairImportFbx.py`, `HairMaterials.py`, `FbxMaterialMap.py`, `ImportQuaternius.py`, `DiagnosePose.py`, `Tools/anim/`, `Tools/hairs/`, `Tools/mods/`, `ModSources/`, `AnimSources/`, `Config/LPSandbox.ini`, `Tools/debug/*_sandbox.bat`, `Docs/Modding/FullGuides/Creating_Asset_Mods.md`, `Docs/Modding/ShortGuides/explanation.lphair` (contenu disque déplacé, non supprimé, vers `D:\Game\LifePlay_Backup_5.5\DC3\deplaces_du_jeu`) ; références `SplitModules.ps1`, `LPModInfoSubsystem.h`, `BuildDebug.ps1`, `explanation.lpmod`, `README.md`, `.gitignore` ; docs (`21_Sandbox_Addon.md`, `09_Commandes_Utiles.md`, `13_Protocole_Coiffure.md`, `16_H3_Mods_Paquets.md`, `10_Protocole_Operations.md`, `23_Cycle_De_Vie_Mod.md`, `20_Chaine_Animations.md`, `README.md`, `etat/sandbox/S3/S3-INT.md`, `etat/sandbox/DC3/*`) ; addon `D:\Game\LifePlay-Sandbox` : atelier de mods (`Tools/*`, `Tools/Roots.ps1` nouveau, `ModSources`, `AnimSources`, lanceurs, `Docs/Modding`, `Config/LPSandbox.ini` section `[Workshop]`, `.gitignore`), `LPAnimCatalogCommandlet.cpp` (`-Livraison=` obligatoire), `LPAnimMeasurePaths.cpp/.h` et `LPSandboxConfig.cpp` (replis vers le jeu retirés) (commits `1f4ec1f` et `79f0205b7281152dde7cd088086d8c813e0b20f5`, branche `lot/DC3-atelier`, tag `lot-DC3-valide`) | lot DC3 : l'atelier de mods vit dans l'addon ; cuisson de `lpr_anim_freeanims` depuis l'addon identique au mod publié (29 fichiers, SHA-256) ; tournée freeanims identique à SB2-REF (DC3-VAL PASS) ; piège UBT `Makefile.bin` (cache généré sans addon) traité. Motif : le jeu public ne doit plus embarquer l'outillage ni les sources de mods du dépôt privé `LifePlay-Sandbox` | pin `97c0d13` → `79f0205` (hash complet `79f0205b7281152dde7cd088086d8c813e0b20f5`) | `.\Tools\Restore.ps1 -Lot DC3` (jeu ; remettre aussi le contenu de `DC3\deplaces_du_jeu`) et `.\Tools\Restore.ps1 -Lot DC3-addon -ProjectRoot D:\Game\LifePlay-Sandbox` ; pin `97c0d13` |
|
||||
| CHG-235 | 2026-10-07 | DC2 | jeu : `Source/LifePlayEditorTools/` (13 sources retirées : `LPAnimEvalCommandlet`, `LPAnimTwistCommandlet`, `LPAnimCatalogCommandlet`, `LPAnimIdentifyCommandlet`, `LPAnimChecks`, `LPHairFromStaticMeshCommandlet` en `.h/.cpp`, `LPAnimMethodSelect.h`), `LifePlayEditorTools.Build.cs`, docs (`21_Sandbox_Addon.md`, `20_Chaine_Animations.md`, `09_Commandes_Utiles.md`, `08_Guide_Developpeur.md`, `etat/sandbox/S3/S3-INT.md`, `etat/sandbox/DC2/*`) ; addon `D:\Game\LifePlay-Sandbox` : module `LPModTools` (nouveau), `LPAnimMeasurePaths.h/.cpp` (nouveau), `LPSkeletonProfile.cpp`, `LPPoseMeasure.cpp`, `LPAnimMeasure.Build.cs`, `LPSandboxConfig.cpp`, `LPSandbox.uplugin` (commit `97c0d134cd1388c3735add79d77ea98817e89fbe`, branche `lot/DC2-modtools`, tag `lot-DC2-valide`) | lot DC2 : les commandlets de mods (`LPAnimEval`, `LPAnimTwist`, `LPAnimCatalog`, `LPAnimIdentify`, `LPHairFromStaticMesh`) passent dans le module éditeur `LPModTools` de l'addon ; le jeu compile sans l'addon (DC2-BUILD) et démarre sans lui (DC2-VAL E5) ; résultats identiques à SB2-REF (DC2-VAL PASS). Motif : le jeu public ne doit plus dépendre du dépôt privé `LifePlay-Sandbox` pour son outillage éditeur | pin `3e9c32f` → `97c0d13` (hash complet `97c0d134cd1388c3735add79d77ea98817e89fbe`) | `.\Tools\Restore.ps1 -Lot DC2` (jeu) et `.\Tools\Restore.ps1 -Lot DC2-addon -ProjectRoot D:\Game\LifePlay-Sandbox` ; pin `3e9c32f` |
|
||||
| CHG-234 | 2026-10-07 | DC1 | jeu : `Plugins/LPAnimCore/*` (nouveau plugin : `LPAnimRedirect.*`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp`), `LifePlay.uproject`, `Source/LifePlay/LifePlay.Build.cs`, docs (`21_Sandbox_Addon.md`, `20_Chaine_Animations.md`, `07_Architecture_5.5.md`, `etat/sandbox/S3/S3-INT.md`, `etat/sandbox/DC1/*`) ; addon `D:\Game\LifePlay-Sandbox` : `LPSandbox.uproject`, `LPSandbox.uplugin`, `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h`, `LPSandbox.Build.cs`, suppression de `LPAnimRedirect.*`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp` (commit `3e9c32f1f27b1abdaf7731dc90496c27ee061718`, branche `lot/DC1-animcore`, tag `lot-DC1-valide`) | lot DC1 : `LPAnimRedirect` et `LPCoupleSim` rapatriés dans le plugin `LPAnimCore` du jeu, `LPAnimMeasure` réduit à la mesure ; compilation PASS, DC1-VAL PASS, contrôle visuel utilisateur 2026-10-07 ; report DC0 SetUALRoot traité. Motif : le jeu public doit compiler sans le dépôt privé `LifePlay-Sandbox` (premier pas du découplage) | pin `16905c6` → `3e9c32f` (hash complet `3e9c32f1f27b1abdaf7731dc90496c27ee061718`) | `.\Tools\Restore.ps1 -Lot DC1` (jeu) et `.\Tools\Restore.ps1 -Lot DC1-addon -ProjectRoot D:\Game\LifePlay-Sandbox` ; pin `16905c6` |
|
||||
|
|
|
|||
196
Docs/Migration/etat/VQ/VQ0/VQ0-CART.md
Normal file
196
Docs/Migration/etat/VQ/VQ0/VQ0-CART.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# VQ0-CART — Cartographie des réglages classés Video dans Settings
|
||||
|
||||
**Fiche :** VQ0-T1
|
||||
**Rôle :** A-CART
|
||||
**Date :** 07 octobre 2026
|
||||
**Workspace :** `d:\Game\LifePlay-master 5.5`
|
||||
**Statut :** Validé — cartographie complète
|
||||
|
||||
---
|
||||
|
||||
## 1. Contexte et Objectifs
|
||||
|
||||
Dans le cadre du chantier **VQ0** (Refonte et rationalisation des réglages Vidéo / Graphismes), l'objectif de cette cartographie est d'auditer et répertorier l'ensemble des éléments d'interface qui se retrouvent classés sous l'onglet **Video** du panneau des réglages (`MainMenu.SettingsScrollBox`).
|
||||
|
||||
Le système actuel repose sur deux mécanismes complémentaires :
|
||||
1. `ULPSettingsSubsystem` (introduit en **CHG-158**, ajusté en **CHG-160**) : organise les widgets existants du Blueprint `MainMenu` en onglets dynamiques (`General`, `Audio`, `Video`, `Keyboard`, `Lewd`, `Misc`) sans modifier les Blueprints.
|
||||
2. `ULPColorGradeSubsystem` (introduit en **CHG-161**, stylisé en **CHG-163**) : injecte dynamiquement des curseurs de colorimétrie post-traitement (`LPColor*`) au bas de la liste `SettingsScrollBox`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Analyse de `LPSettingsSubsystem.cpp`
|
||||
|
||||
### 2.1 Méthode de classification : `CategoryOf`
|
||||
|
||||
La fonction `LPSettingsUi::CategoryOf(const UWidget* Widget)` inspecte chaque enfant de `SettingsScrollBox` pour déterminer son onglet de destination.
|
||||
|
||||
L'aiguillage vers l'onglet **Video** (`TabVideo = 2`) s'opère selon deux règles successives :
|
||||
|
||||
1. **Règle explicite par préfixe :**
|
||||
```cpp
|
||||
if (Widget && Widget->GetName().StartsWith(TEXT("LPColor")))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
```
|
||||
Tout widget dont le nom C++/UMG commence par `LPColor` est immédiatement dirigé vers `TabVideo`.
|
||||
|
||||
2. **Règle textuelle par mots-clés (`RowText`) :**
|
||||
Si le widget ne commence pas par `LPColor` et n'a pas été capturé par les mots-clés Audio (`volume`, `audio`, `sound`, `music`), `CategoryOf` convertit le texte extrait en minuscules et teste les motifs suivants :
|
||||
```cpp
|
||||
if (Text.Contains(TEXT("fps")) || Text.Contains(TEXT("screen")) || Text.Contains(TEXT("shadow")) || Text.Contains(TEXT("quality"))
|
||||
|| Text.Contains(TEXT("fullscreen")) || Text.Contains(TEXT("window")) || Text.Contains(TEXT("toon")) || Text.Contains(TEXT("depth of field"))
|
||||
|| Text.Contains(TEXT("censor")) || Text.Contains(TEXT("morph")) || Text.Contains(TEXT("dog")) || Text.Contains(TEXT("jiggle"))
|
||||
|| Text.Contains(TEXT("resolution")) || Text.Contains(TEXT("vsync")) || Text.Contains(TEXT("brightness")))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Extraction du texte : `RowText`
|
||||
|
||||
La fonction `RowText(const UWidget* Widget)` cumule tout le texte associé à la ligne :
|
||||
- La propriété réflexive `SettingText` (`FTextProperty`), utilisée par les UserWidgets `SettingSlider` et `SettingCheckbox`.
|
||||
- L'infobulle native (`Widget->GetToolTipText()`).
|
||||
- Une exploration récursive en profondeur de tous les sous-widgets (`UTextBlock::GetText()`, `UUserWidget::GetRootWidget()`, `UPanelWidget::GetChildAt()`).
|
||||
|
||||
### 2.3 Mécanisme `EnsureTabs` et `ApplyRows`
|
||||
|
||||
- **`EnsureTabs(UScrollBox* Scroll)`** :
|
||||
- Identifie le parent `UCanvasPanel` de `SettingsScrollBox`.
|
||||
- Crée la barre d'onglets `LPSettingsTabs` (`UHorizontalBox`) au-dessus de la boîte de défilement (décalage Y de -46 px).
|
||||
- Crée les boutons d'onglets : `General`, `Audio`, `Video`, `Keyboard`, `Lewd`, `Misc`.
|
||||
- Instancie `LPSettingsKeyboard` (`UNativeWidgetHost`) pour héberger la page dédiée au remappage clavier (`ULPKeyboardSubsystem`).
|
||||
- Active l'onglet initial `TabGeneral` (0) et exécute `ApplyRows()`.
|
||||
|
||||
- **`ApplyRows()`** :
|
||||
- Met à jour la couleur du texte des onglets (doré `(1.0, 0.92, 0.45)` pour l'onglet actif, gris clair `(0.75, 0.75, 0.75)` pour les inactifs).
|
||||
- Parcourt tous les enfants directs de `SettingsScrollBox` :
|
||||
- Masque le widget legacy `LPKeyboardRow` (`Visibility = Collapsed`).
|
||||
- Applique `RenameVolumes` (renomme `Volume - Sex` en `Volume - Dialogue` et `Volume - Sex Moans` en `Volume - SFX`).
|
||||
- Évalue `Category = CategoryOf(Child)`.
|
||||
- Si `Category < 0` (ex. bouton `BackButton`), le widget reste visible en permanence.
|
||||
- Si `Category == ActiveTab` (et que l'on n'est pas sur l'onglet Keyboard), le widget est rendu **Visible**, sinon **Collapsed**.
|
||||
- Si l'onglet actif est `TabKeyboard`, la liste `SettingsScrollBox` n'affiche aucune ligne métier et l'hôte `KeyboardHost` affiche l'arbre Slate natif de `ULPKeyboardSubsystem`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Analyse de `LPColorGradeSubsystem`
|
||||
|
||||
`ULPColorGradeSubsystem` est un `UGameInstanceSubsystem` qui gère un volume de post-traitement global non borné (`APostProcessVolume`, `bUnbound = true`, priorité 100.0).
|
||||
|
||||
Dans `EnsureControls()`, il vérifie la présence de `LPColorBrightness` dans `SettingsScrollBox`. S'il est absent, il injecte 5 lignes créées dynamiquement en C++ via `AddRow` :
|
||||
|
||||
| Nom widget | Libellé | Type | Min | Max | Pas | Défaut | Handler C++ | Info-bulle / Rôle | Propriété FPostProcessSettings |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| `LPColorBrightness` | Brightness | Slider + Readout | -2.0 | 2.0 | 0.05 | 0.0 | `OnBrightnessChanged` | Exposure compensation. -1 is twice as dark, +1 is twice as bright. | `AutoExposureBias` |
|
||||
| `LPColorSaturation` | Saturation | Slider + Readout | 0.0 | 2.0 | 0.05 | 1.0 | `OnSaturationChanged` | Color intensity. 1 is unchanged, 0 is gray. | `ColorSaturation` (XYZ) |
|
||||
| `LPColorContrast` | Contrast | Slider + Readout | 0.0 | 2.0 | 0.05 | 1.0 | `OnContrastChanged` | Range between light and dark. 1 is unchanged. | `ColorContrast` (XYZ) |
|
||||
| `LPColorWhiteBalance` | White Balance | Slider + Readout | 1500 | 15000 | 50.0 | 6500.0 | `OnWhiteBalanceChanged` | Color temperature in kelvin. 6500 is daylight. | `WhiteTemp` (`TEMP_WhiteBalance`) |
|
||||
| `LPColorTint` | Tint | Slider + Readout | -1.0 | 1.0 | 0.01 | 0.0 | `OnTintChanged` | Magenta to green. 0 is unchanged. | `WhiteTint` |
|
||||
|
||||
### Caractéristiques d'injection :
|
||||
- **Insertion :** `Scroll->AddChild(Row)` — les widgets sont systématiquement ajoutés **à la fin** de la liste d'enfants de `SettingsScrollBox`.
|
||||
- **Héritage du style :** `LPColorGrade::FindLook` inspecte les lignes existantes pour copier la police (`Regular 20` avec contour noir de 2 px) et le style Slate du curseur (`FSliderStyle` copié depuis `SettingSlider`).
|
||||
- **Persistance :** Écrit et relit `Saved/Config/Windows/LPColorGrade.ini` (section `[LPColorGrade]`), sans aucune dépendance envers `GameSettings.sav`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Relevé historique et analyse des modifications (Docs / Journal)
|
||||
|
||||
L'analyse de `Docs/Migration/Journal_Modifications.md` et des historiques de commits montre la genèse des réglages :
|
||||
- **CHG-156 (2026-10-05) :** Ajout initial d'un bouton Clavier directement dans `SettingsScrollBox`.
|
||||
- **CHG-158 (2026-10-05) :** Séparation en 6 onglets au-dessus de `SettingsScrollBox` via `LPSettingsSubsystem` ; les lignes existantes du Blueprint sont classées selon leur texte par `CategoryOf`.
|
||||
- **CHG-159 (2026-10-05) :** Bouton `Settings` réintégré au menu titre sous `Mod Manager` via `LPSettingsTitleButton`.
|
||||
- **CHG-160 (2026-10-05) :** Ordre final des onglets (`General`, `Audio`, `Video`, `Keyboard`, `Lewd`, `Misc`), renommage des libellés audio crus.
|
||||
- **CHG-161 (2026-10-05) :** `LPColorGradeSubsystem` : correction de l'image brûlée/trop blanche via Color Grading natif Unreal (exposition et balance des blancs) plutôt qu'un pipeline externe.
|
||||
- **CHG-163 (2026-10-05) :** Harmonisation graphique des curseurs et polices sur `SettingSlider`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Cartographie exhaustive des lignes classées "Video"
|
||||
|
||||
L'inspection directe des binaires d'assets (`MainMenu.uasset`, `SettingSlider.uasset`, `SettingCheckbox.uasset`, `GameSettings.uasset`) permet d'établir la liste complète des widgets dirigés vers l'onglet Video :
|
||||
|
||||
| Nom widget UMG | Libellé affiché (`SettingText` / Text) | Type UMG | Source | Mot-clé `CategoryOf` | Rôle technique / Variable sous-jacente |
|
||||
|---|---|---|---|---|---|
|
||||
| `FullscreenButton` (avec `FullscreenText`) | `Fullscreen` (bascule `Borderless Window` / `Windowed`) | `UButton` + `UTextBlock` | BP (`MainMenu`) | `fullscreen`, `window` | Bascule le mode fenêtré (`EWindowMode`, `GameUserSettings`) |
|
||||
| `PostProcessAAQuality` | `AA Quality` | `SettingSlider_C` | BP (`MainMenu`) | `quality` | Qualité de l'anti-aliasing (`r.PostProcessAAQuality`) |
|
||||
| `PostProcessQuality` | `PP Quality (FPS intensive)` | `SettingSlider_C` | BP (`MainMenu`) | `quality`, `fps`, `depth of field` (tooltip) | Évolutivité post-traitement globale (`sg.PostProcessQuality`) |
|
||||
| `ScreenPercentage` | `Screen Percentage` | `SettingSlider_C` | BP (`MainMenu`) | `screen`, `resolution` (tooltip) | Échelle de résolution 3D interne (`r.ScreenPercentage`) |
|
||||
| `ShadowQuality` | `Shadow Quality` | `SettingSlider_C` | BP (`MainMenu`) | `shadow`, `quality`, `resolution` (tooltip) | Évolutivité des ombres (`sg.ShadowQuality`) |
|
||||
| `ShowFPS` | `Show FPS` (`Show Frame Per Second`) | `SettingCheckbox_C` | BP (`MainMenu`) | `fps` | Affichage du compteur d'images/sec (`stat FPS`) |
|
||||
| `ToonShading` | `Toon Shading` | `SettingCheckbox_C` | BP (`MainMenu`) | `toon` | Matériau de post-traitement Cel-Shading 2D |
|
||||
| `Censore` | `YouTube Censore` | `SettingCheckbox_C` | BP (`MainMenu`) | `censor` | Pixellisation mosaïque de censure vidéo/SFW |
|
||||
| `CPUSkinning` | `Tick If Morphs Don't Work` | `SettingCheckbox_C` | BP (`MainMenu`) | `morph` | Force le calcul des morph targets sur CPU (`r.MorphTarget.Mode`) |
|
||||
| `DisableFurEffect` | `Invisible Dog Fix` | `SettingCheckbox_C` | BP (`MainMenu`) | `dog` | Contournement de rendu du pelage des chiens (cartes graphiques instables) |
|
||||
| `SpinBoxPhysicsMult` (avec `TextBlock_13`) | `Boob Jiggle Strength` | `UHorizontalBox` + `USpinBox` | BP (`MainMenu`) | `jiggle` | Multiplicateur physique pour la dynamique secondaire de poitrine |
|
||||
| `SpinBox_0` (avec `TextBlock_14`) | `Depth of Field Strength` | `UHorizontalBox` + `USpinBox` | BP (`MainMenu`) | `depth of field` | Intensité du flou de profondeur de champ dans les chambres |
|
||||
| `ShadeDepth` (avec `TextBlock_16`) | `Toon Shade Depth` | `UHorizontalBox` + `USpinBox` | BP (`MainMenu`) | `toon` | Seuil de profondeur pour le rendu Toon Shading |
|
||||
| *`NewAnimationsOnly` (Cas litigieux)* | `New Animations Only` | `SettingCheckbox_C` | BP (`MainMenu`) | `quality` (involontaire via tooltip) | **Conflit :** Filtrage d'animations capturé par Video à cause de l'infobulle |
|
||||
| `LPColorBrightness` | `Brightness` | `UHorizontalBox` + `USlider` | `LPColor` | `LPColor*` (et `brightness`) | Compensation d'exposition globale (`AutoExposureBias`) |
|
||||
| `LPColorSaturation` | `Saturation` | `UHorizontalBox` + `USlider` | `LPColor` | `LPColor*` | Saturation colorimétrique (`ColorSaturation`) |
|
||||
| `LPColorContrast` | `Contrast` | `UHorizontalBox` + `USlider` | `LPColor` | `LPColor*` | Contraste de l'image (`ColorContrast`) |
|
||||
| `LPColorWhiteBalance` | `White Balance` | `UHorizontalBox` + `USlider` | `LPColor` | `LPColor*` | Température des blancs (`WhiteTemp`, Kelvin) |
|
||||
| `LPColorTint` | `Tint` | `UHorizontalBox` + `USlider` | `LPColor` | `LPColor*` | Teinte magenta/vert (`WhiteTint`) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Analyse des conflits potentiels
|
||||
|
||||
### 6.1 Conflit `quality`
|
||||
1. **Faux-positif `NewAnimationsOnly` :**
|
||||
- Le widget `NewAnimationsOnly` est une option de gameplay/animation (« Avoid using the old converted animations... »).
|
||||
- Son infobulle se termine par : *« For those that prefer quality to quantity. »*
|
||||
- Comme `RowText` agrège l'infobulle et que `quality` est testé dans `TabVideo` **avant** le mot-clé `animation` de `TabGeneral`, cette case se retrouve classée à tort dans l'onglet **Video** !
|
||||
2. **Ambiguïté sur les paramètres d'évolutivité (Scalability) :**
|
||||
- Les réglages BP existants utilisent `PP Quality` (`sg.PostProcessQuality`) et `AA Quality` (`r.PostProcessAAQuality`).
|
||||
- L'introduction de nouveaux réglages `LPVideo*` gérant la qualité globale (`Overall Quality`) ou d'autres sous-groupes (`Texture Quality`, `Effects Quality`, `View Distance Quality`) risque de créer une confusion ou une double commande avec les curseurs BP actuels.
|
||||
|
||||
### 6.2 Conflit `shadow`
|
||||
1. **Redondance directe sur la qualité des ombres :**
|
||||
- Le Blueprint fournit déjà `ShadowQuality` (`sg.ShadowQuality` via `SettingSlider`).
|
||||
- Si `LPVideo*` injecte un curseur ou sélecteur d'ombres sans masquer ou synchroniser le widget BP existant, les deux widgets entreront en concurrence directe.
|
||||
2. **Homonymie `ShadeDepth` :**
|
||||
- Le widget `ShadeDepth` porte le libellé `Toon Shade Depth`. Bien qu'il contienne la racine *shade*, il est actuellement capté par le mot-clé `toon`. Il ne règle pas les ombres géométriques mais le seuil de découpe du cel-shading.
|
||||
|
||||
### 6.3 Conflit `brightness`
|
||||
1. **Concurrence sur la luminosité et la luminance d'affichage :**
|
||||
- `LPColorBrightness` agit sur `AutoExposureBias` (valeur neutre 0.0, plage [-2.0, 2.0]) dans un volume de post-traitement non borné.
|
||||
- Si `LPVideo*` introduit un réglage standard de luminosité d'écran (ex. Gamma `r.Color.Mid`, courbe de gamma d'affichage ou compensation d'exposition), il y aura un conflit de superposition avec `LPColorBrightness`.
|
||||
- Il conviendra soit de fusionner la notion d'exposition/luminosité sous une bannière unique, soit de clairement distinguer `Brightness (Exposition 3D)` de `Gamma d'affichage`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Proposition d'emplacement d'injection pour `LPVideo*`
|
||||
|
||||
### 7.1 Recommandation : Injection AVANT `LPColor*`
|
||||
|
||||
L'injection des futurs composants `LPVideo*` doit se positionner **AVANT les lignes `LPColor*`**, pour les raisons suivantes :
|
||||
|
||||
1. **Hiérarchie ergonomique standard de l'industrie :**
|
||||
- Dans tous les jeux vidéo modernes sous Unreal Engine, la structure conventionnelle du menu Vidéo / Affichage est :
|
||||
1. **Affichage / Écran :** Mode fenêtré, Résolution d'écran, VSync, Limite FPS.
|
||||
2. **Qualité graphique / Évolutivité :** Préréglage global, Échelle de rendu, Ombres, Post-traitement, Anti-Aliasing, Textures.
|
||||
3. **Effets visuels spécifiques :** Cel-Shading, Profondeur de champ, Flou de mouvement.
|
||||
4. **Étalonnage / Colorimétrie (Calibration) :** Luminosité/Exposition, Contraste, Saturation, Balance des blancs.
|
||||
- Placer `LPColor*` en queue de menu respecte cette logique naturelle où l'étalonnage colorimétrique constitue la section finale de réglage fin de l'image.
|
||||
|
||||
2. **Cohérence d'implémentation technique :**
|
||||
- `LPColorGradeSubsystem` s'exécute déjà en ajoutant ses lignes à la fin (`Scroll->AddChild(Row)`).
|
||||
- En faisant injecter `LPVideo*` en amont (soit au-dessus de `LPColor*`, soit à l'emplacement des curseurs BP que `LPVideo` viendra remplacer ou compléter), `LPColor*` conservera son statut naturel de bloc de calibration terminal.
|
||||
- Si des options BP existantes (`ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality`, `ScreenPercentage`) sont retirées ou remplacées par `LPVideo*`, celles-ci doivent être masquées (`SetVisibility(ESlateVisibility::Collapsed)`) par le sous-système au moment de l'injection de `LPVideo*`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Synthèse récapitulative
|
||||
|
||||
| Périmètre | Nombre de lignes | Origine | Statut |
|
||||
|---|---|---|---|
|
||||
| Réglages d'affichage BP | 1 (`FullscreenButton`) | Blueprint `MainMenu` | Valide, à conserver en tête de menu |
|
||||
| Curseurs Scalability BP | 4 (`ScreenPercentage`, `PostProcessQuality`, `PostProcessAAQuality`, `ShadowQuality`) | Blueprint `MainMenu` | Fonctionnels mais redondants avec un futur menu vidéo complet |
|
||||
| Cases à cocher / Effets BP | 4 (`ShowFPS`, `ToonShading`, `Censore`, `CPUSkinning`, `DisableFurEffect`) | Blueprint `MainMenu` | Spécifiques LifePlay, à conserver |
|
||||
| SpinBoxes secondaires BP | 3 (`Boob Jiggle Strength`, `Depth of Field Strength`, `Toon Shade Depth`) | Blueprint `MainMenu` | Réglages visuels spécifiques |
|
||||
| Faux-positif à corriger | 1 (`NewAnimationsOnly`) | Blueprint `MainMenu` | À reclasser dans `TabGeneral` (conflit mot-clé `quality`) |
|
||||
| Lignes Color Grading C++ | 5 (`LPColorBrightness`, `Saturation`, `Contrast`, `WhiteBalance`, `Tint`) | `LPColorGradeSubsystem` | Valides, stables, à maintenir en bloc terminal |
|
||||
| **Emplacement `LPVideo*`** | **Nouveau bloc** | `LPVideo*` (C++) | **À intercaler entre les réglages BP et le bloc `LPColor*`** |
|
||||
50
Docs/Migration/etat/VQ/VQ0/VQ0-VAL.md
Normal file
50
Docs/Migration/etat/VQ/VQ0/VQ0-VAL.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# VQ0-VAL — Validation de la cartographie Video (VQ0-T1)
|
||||
|
||||
**Fiche :** VQ0-T2
|
||||
**Rôle :** A-VAL
|
||||
**Date :** 07 octobre 2026
|
||||
**Amont :** `Docs/Migration/etat/VQ/VQ0/VQ0-CART.md`
|
||||
**Modèle prévu :** composer-2.5-fast (M0)
|
||||
**Modèle utilisé :** composer-2.5-fast (M0)
|
||||
|
||||
**Verdict global : PASS**
|
||||
|
||||
---
|
||||
|
||||
## 1. Critères contrôlés
|
||||
|
||||
| # | Critère | Méthode | Résultat |
|
||||
|---|---|---|---|
|
||||
| C1 | CART non vide (inventaire Video, conflits, synthèse) | Lecture `VQ0-CART.md` §§ 2–8 | PASS — 18 lignes inventoriées + bloc `LPColor*` (5) + faux-positif `NewAnimationsOnly` |
|
||||
| C2 | Place d'injection `LPVideo*` explicite (avant `LPColor*`) | CART §7 + cohérence avec le code d'injection actuel | PASS — recommandation documentée ; `LPColor*` reste en queue via `AddChild` |
|
||||
| C3 | Chemins code cités et vérifiables | Relecture des sources C++ | PASS — voir §2 |
|
||||
| C4 | Noms de widgets BP du relevé §5 | Scan ASCII de `Content/UI/MainMenu.uasset` | PASS — 14 noms BP attendus présents |
|
||||
|
||||
---
|
||||
|
||||
## 2. Recoupement code (chemins cités)
|
||||
|
||||
| Sujet | Fichier | Lignes (approx.) | Constat |
|
||||
|---|---|---|---|
|
||||
| Préfixe `LPColor*` → onglet Video | `Source/LifePlay/LPSettingsSubsystem.cpp` | `CategoryOf` : règle `StartsWith("LPColor")`, `TabVideo = 2` | Conforme au CART §2.1 |
|
||||
| Mots-clés Video (`quality`, `shadow`, `fps`, …) | idem | `CategoryOf` : second bloc `Text.Contains` | Conforme ; ordre **Video avant General** → faux-positif `NewAnimationsOnly` (tooltip « quality ») plausible |
|
||||
| Agrégation `RowText` | idem | `RowText` (SettingText, tooltip, parcours récursif) | Conforme au CART §2.2 |
|
||||
| Onglets / visibilité | idem | `EnsureTabs`, `ApplyRows`, `Tick` sur `SettingsScrollBox` | Conforme au CART §2.3 |
|
||||
| Injection `LPColor*` en fin de liste | `Source/LifePlay/LPColorGradeSubsystem.cpp` | `AddRow` → `Scroll->AddChild(Row)` ; `EnsureControls` idempotent sur `LPColorBrightness` | Conforme au CART §3 ; confirme que `LPVideo*` devra **insérer** ou **masquer/replacer** les lignes BP, pas seulement `AddChild` en queue |
|
||||
| Persistance colorimétrie | `Source/LifePlay/LPColorGradeSubsystem.h/.cpp` | `Saved/Config/Windows/LPColorGrade.ini` | Conforme au CART §3 |
|
||||
|
||||
---
|
||||
|
||||
## 3. Réserves mineures (non bloquantes)
|
||||
|
||||
- **CART §8** : la ligne « Cases à cocher / Effets BP » indique « 4 » widgets alors que le tableau en liste cinq (`ShowFPS` … `DisableFurEffect`) — coquille de décompte, pas d'impact sur la cartographie.
|
||||
- **Scalability BP** : les CVars (`sg.ShadowQuality`, `sg.PostProcessQuality`, `r.PostProcessAAQuality`, `r.ScreenPercentage`) ne sont pas relues dans le C++ LifePlay ; l'association repose sur les `SettingSlider` Blueprint — acceptable pour VQ0-T1, à confirmer en implémentation `LPVideo*`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Décision
|
||||
|
||||
| Fiche | Agent | Livrable | Verdict |
|
||||
|---|---|---|---|
|
||||
| VQ0-T1 | A-CART | `VQ0-CART.md` | Entrée amont validée |
|
||||
| VQ0-T2 | A-VAL | `VQ0-VAL.md` | **PASS** → VQ0-T3 (A-DOC) autorisé |
|
||||
125
Docs/Migration/etat/VQ/VQ1/VQ1-CART.md
Normal file
125
Docs/Migration/etat/VQ/VQ1/VQ1-CART.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# VQ1-CART — Cartographie et audit de conformité `DefaultScalability.ini`
|
||||
|
||||
- **Date :** 2026-10-07
|
||||
- **Fiche :** VQ1-T3 | **Agent :** A-CART | **Rôle :** Cartographie & Audit de conformité VQ1
|
||||
- **Modèle prévu :** `gemini-3.8-flash-high` (M1) | **Modèle utilisé :** `gemini-3.8-flash-high` (M1)
|
||||
- **Workspace :** `d:\Game\LifePlay-master 5.5`
|
||||
- **Référence spécification :** `Docs/Migration/etat/VQ/VQ1/VQ1-SPEC.md` (VQ1-T1)
|
||||
- **Référence implémentation :** `Docs/Migration/etat/VQ/VQ1/VQ1-IMPL.md` (VQ1-T2)
|
||||
- **Référence verrous moteur :** `Config/DefaultEngine.ini` (lots L6a / L7c)
|
||||
- **Statut :** **OK** (0 hit bloquant, 0 OUBLI) — **PASS**
|
||||
|
||||
---
|
||||
|
||||
## 1. Périmètre et Objectifs
|
||||
|
||||
L'objectif de cette cartographie (fiche **VQ1-T3**) est d'auditer le fichier de configuration de scalabilité nouvellement créé `Config/DefaultScalability.ini` par rapport aux verrous stricts du moteur et de rendu définis dans `Config/DefaultEngine.ini` (notamment lots **L6a** et **L7c**).
|
||||
|
||||
Le contrat de conformité exige :
|
||||
1. Recherche ciblée (Grep) des 5 termes/variables interdits ou verrouillés :
|
||||
- `DynamicGlobalIllumination`
|
||||
- `Virtual.Enable`
|
||||
- `AntiAliasingMethod`
|
||||
- `Lumen`
|
||||
- `ReflectionMethod`
|
||||
2. Confrontation systématique avec les verrous définis dans `Config/DefaultEngine.ini` (sections `[/Script/Engine.RendererSettings]` et `[/Script/WindowsTargetPlatform.WindowsTargetSettings]`, lot L7c / L6a).
|
||||
3. Classification stricte en **OK** ou **OUBLI** (critère d'acceptation : **0 hit bloquant**).
|
||||
4. Émission du verdict **PASS / FAIL**.
|
||||
|
||||
---
|
||||
|
||||
## 2. État des verrous dans `Config/DefaultEngine.ini` (Lots L6a / L7c)
|
||||
|
||||
Dans `Config/DefaultEngine.ini`, les paramètres de rendu ont été explicitement verrouillés au niveau projet (`SetByProjectSetting`, priorité supérieure à `SetByScalability`) pour garantir la parité stricte avec le rendu validé UE 4.27 et prévenir les instabilités ou scintillements de maillages :
|
||||
|
||||
```ini
|
||||
[/Script/Engine.RendererSettings]
|
||||
; Lot L6a (ERR-032) : r.DefaultFeature.AntiAliasing est ignoree par UE5 (variable fantome),
|
||||
; le jeu tournait donc en TSR (defaut UE5 = 4). TAA (2), comme en 4.27 : TSR fait scintiller
|
||||
; les maillages deformes par morphs (personnages).
|
||||
r.AntiAliasingMethod=2
|
||||
; Lot L7c : rendu 5.5 rendu explicite, aux valeurs effectivement jouees et validees (identiques aux
|
||||
; valeurs par defaut du moteur 5.5, qui correspondent au rendu 4.27) : pas d'illumination globale
|
||||
; dynamique (Lumen), reflets SSR, pas d'ombres virtuelles. Ainsi un changement de valeur par defaut du
|
||||
; moteur ou un modele de projet UE5 ne modifie pas le rendu sans decision.
|
||||
r.DynamicGlobalIlluminationMethod=0
|
||||
r.ReflectionMethod=2
|
||||
r.Shadow.Virtual.Enable=0
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
r.TextureStreaming=True
|
||||
r.DefaultFeature.AmbientOcclusionStaticFraction=False
|
||||
r.AllowStaticLighting=True
|
||||
r.MinScreenRadiusForCSMDepth=0.010000
|
||||
r.AllowOcclusionQueries=True
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
; Lot L7c : D3D12 (SM5), RHI par defaut de UE 5.5 sur lequel toutes les sessions de validation ont ete
|
||||
; jouees (UE 4.27 utilisait D3D11).
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
```
|
||||
|
||||
Ces réglages interdisent formellement à `Config/DefaultScalability.ini` d'introduire :
|
||||
- Des ombres virtuelles (`r.Shadow.Virtual.*` / VSM),
|
||||
- Une méthode d'illumination dynamique (`r.DynamicGlobalIlluminationMethod`, Lumen),
|
||||
- Une méthode de réflexion alternative (`r.ReflectionMethod`),
|
||||
- Un changement de la méthode d'anti-aliasing (`r.AntiAliasingMethod`, TSR étant banni au profit de TAA = 2).
|
||||
|
||||
---
|
||||
|
||||
## 3. Audit Grep de `Config/DefaultScalability.ini`
|
||||
|
||||
Une recherche insensible à la casse a été exécutée sur l'intégralité du fichier `Config/DefaultScalability.ini` pour les cinq motifs sous revue :
|
||||
|
||||
| Clé / Motif recherché | Présence dans `DefaultScalability.ini` | Nombre de hits | Statut | Classification |
|
||||
|---|---|---:|---|---|
|
||||
| `DynamicGlobalIllumination` | Absent | 0 | Conforme | **OK** |
|
||||
| `Virtual.Enable` | Absent | 0 | Conforme | **OK** |
|
||||
| `AntiAliasingMethod` | Absent | 0 | Conforme | **OK** |
|
||||
| `Lumen` | Absent | 0 | Conforme | **OK** |
|
||||
| `ReflectionMethod` | Absent | 0 | Conforme | **OK** |
|
||||
|
||||
### Synthèse des motifs audités
|
||||
- **Hits totaux trouvés :** 0
|
||||
- **Hits bloquants :** 0
|
||||
- **OUBLI détecté :** 0
|
||||
|
||||
---
|
||||
|
||||
## 4. Inventaire et vérification des sections implémentées
|
||||
|
||||
Le fichier `Config/DefaultScalability.ini` comprend exactement 16 sections correspondant aux 4 groupes autorisés par `VQ1-SPEC.md` :
|
||||
|
||||
| Groupe de scalabilité | Sections présentes | Cvars configurées par niveau | Conformité SPEC §4 |
|
||||
|---|---|---|---|
|
||||
| `sg.ShadowQuality` | `[ShadowQuality@0]`<br>`[ShadowQuality@1]`<br>`[ShadowQuality@2]`<br>`[ShadowQuality@3]` | `r.Shadow.CSM.MaxCascades`<br>`r.Shadow.MaxResolution`<br>`r.Shadow.MaxCSMResolution`<br>`r.Shadow.RadiusThreshold`<br>`r.Shadow.DistanceScale`<br>`r.Shadow.CSM.TransitionScale`<br>`r.Shadow.PreShadowResolutionFactor` | **OK** (Cascade Shadow Maps traditionnelles, aucune ombre virtuelle) |
|
||||
| `sg.TextureQuality` | `[TextureQuality@0]`<br>`[TextureQuality@1]`<br>`[TextureQuality@2]`<br>`[TextureQuality@3]` | `r.Streaming.MipBias`<br>`r.MaxAnisotropy`<br>`r.Streaming.PoolSize`<br>`r.Streaming.MaxEffectiveScreenSize` | **OK** (Streaming standard conforme) |
|
||||
| `sg.EffectsQuality` | `[EffectsQuality@0]`<br>`[EffectsQuality@1]`<br>`[EffectsQuality@2]`<br>`[EffectsQuality@3]` | `r.TranslucencyLightingVolumeDim`<br>`r.RefractionQuality`<br>`r.SSS.Scale`<br>`r.SSS.SampleSet`<br>`r.DetailMode`<br>`fx.Niagara.QualityLevel` | **OK** (Effets classiques, aucun Lumen/SSR injecté) |
|
||||
| `sg.AntiAliasingQuality` | `[AntiAliasingQuality@0]`<br>`[AntiAliasingQuality@1]`<br>`[AntiAliasingQuality@2]`<br>`[AntiAliasingQuality@3]` | `r.PostProcessAAQuality` | **OK** (Qualité uniquement, la méthode reste fixée à TAA par `DefaultEngine.ini`) |
|
||||
|
||||
### Vérification des groupes interdits
|
||||
- `GlobalIlluminationQuality` : **Absent** (laisse le moteur intact / GI désactivée par L7c).
|
||||
- `ReflectionQuality` : **Absent** (SSR verrouillé par L7c).
|
||||
- `ViewDistanceQuality`, `FoliageQuality`, `LandscapeQuality`, `ShadingQuality` : **Absents** (non surchargés).
|
||||
- Profil `@Cine` : **Absent** (conservé au comportement moteur).
|
||||
|
||||
---
|
||||
|
||||
## 5. Bilan comparatif DefaultScalability.ini vs DefaultEngine.ini L7c
|
||||
|
||||
| Domaine de rendu | Règle `DefaultEngine.ini` (L6a / L7c) | Implémentation `DefaultScalability.ini` | Alignement |
|
||||
|---|---|---|---|
|
||||
| **Global Illumination** | `r.DynamicGlobalIlluminationMethod=0` (Pas de GI dynamique / Lumen) | Aucune cvar GI ni section `GlobalIlluminationQuality` | **OK — Aucun conflit** |
|
||||
| **Ombres** | `r.Shadow.Virtual.Enable=0` (Ombres CSM classiques, pas de VSM) | Exclusivement des cvars CSM (`r.Shadow.CSM.*`, `r.Shadow.MaxResolution`, etc.) | **OK — Aucun conflit** |
|
||||
| **Anti-Aliasing** | `r.AntiAliasingMethod=2` (TAA imposé, TSR interdit car morph jitter) | Uniquement `r.PostProcessAAQuality` (qualité du filtre d'accumulation) | **OK — Aucun conflit** |
|
||||
| **Reflets** | `r.ReflectionMethod=2` (Reflets SSR classiques, pas de Lumen) | Aucune cvar de réflexion injectée | **OK — Aucun conflit** |
|
||||
| **RHI** | `DefaultGraphicsRHI=DefaultGraphicsRHI_DX12` | Sans objet pour la scalabilité | **OK** |
|
||||
|
||||
---
|
||||
|
||||
## 6. Conclusion et Verdict
|
||||
|
||||
L'audit de `Config/DefaultScalability.ini` démontre une stricte conformité aux spécifications `VQ1-SPEC.md` et aux garde-fous de rendu définis lors des lots **L6a** et **L7c** dans `Config/DefaultEngine.ini`. Aucun mot-clé interdit n'est présent dans le fichier.
|
||||
|
||||
- **Hits bloquants :** 0 (seuil requis : 0)
|
||||
- **OUBLI :** 0
|
||||
- **Statut final :** **PASS**
|
||||
48
Docs/Migration/etat/VQ/VQ1/VQ1-IMPL.md
Normal file
48
Docs/Migration/etat/VQ/VQ1/VQ1-IMPL.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# VQ1-IMPL — Implémentation `Config/DefaultScalability.ini`
|
||||
|
||||
**Fiche :** VQ1-T2
|
||||
**Rôle :** A-IMPL-L
|
||||
**Modèle attendu :** claude-sonnet-5-5-high (M2)
|
||||
**Date :** 07 octobre 2026
|
||||
**Workspace :** `d:\Game\LifePlay-master 5.5`
|
||||
**Spécification :** `Docs/Migration/etat/VQ/VQ1/VQ1-SPEC.md`
|
||||
**Statut :** Implémenté — validation en éditeur/jeu à faire (VQ1-VAL)
|
||||
|
||||
---
|
||||
|
||||
## 1. Fichiers
|
||||
|
||||
| Fichier | Action |
|
||||
|---|---|
|
||||
| `Config/DefaultScalability.ini` | **Créé** |
|
||||
| `Docs/Migration/etat/VQ/VQ1/VQ1-IMPL.md` | **Créé** (ce document) |
|
||||
| `Config/DefaultEngine.ini` | **Non modifié** (verrous L7c / L6a intacts) |
|
||||
|
||||
Aucun code C++, Blueprint ni asset modifié.
|
||||
|
||||
## 2. Contenu implémenté
|
||||
|
||||
16 sections, strictement conformes au §4 de la SPEC :
|
||||
|
||||
| Groupe | Sections | Cvars par niveau |
|
||||
|---|---|---|
|
||||
| ShadowQuality | `@0`…`@3` | 7 : `r.Shadow.CSM.MaxCascades`, `r.Shadow.MaxResolution`, `r.Shadow.MaxCSMResolution`, `r.Shadow.RadiusThreshold`, `r.Shadow.DistanceScale`, `r.Shadow.CSM.TransitionScale`, `r.Shadow.PreShadowResolutionFactor` |
|
||||
| TextureQuality | `@0`…`@3` | 4 : `r.Streaming.MipBias`, `r.MaxAnisotropy`, `r.Streaming.PoolSize`, `r.Streaming.MaxEffectiveScreenSize` |
|
||||
| EffectsQuality | `@0`…`@3` | 6 : `r.TranslucencyLightingVolumeDim`, `r.RefractionQuality`, `r.SSS.Scale`, `r.SSS.SampleSet`, `r.DetailMode`, `fx.Niagara.QualityLevel` |
|
||||
| AntiAliasingQuality | `@0`…`@3` | 1 : `r.PostProcessAAQuality` (0 / 2 / 4 / 4) |
|
||||
|
||||
« Overall » : pas de section dédiée ; appliquer le même niveau `N` aux 4 groupes (SPEC §5).
|
||||
|
||||
## 3. Respect des interdits
|
||||
|
||||
Vérification par recherche (insensible à la casse) dans `Config/DefaultScalability.ini` des motifs `virtual`, `lumen`, `globalillum`, `AntiAliasingMethod`, `ReflectionMethod`, `SSR` : **0 occurrence** (commentaires inclus).
|
||||
`git status` sur `Config/` : seul `Config/DefaultScalability.ini` apparaît (non suivi) ; `DefaultEngine.ini` n'est pas modifié.
|
||||
|
||||
## 4. Points à confirmer en validation (VQ1-VAL)
|
||||
|
||||
1. **Valeurs moteur :** confronter chaque valeur du fichier à `Engine/Config/BaseScalability.ini` de l'installation UE 5.5 (les paliers de la SPEC proviennent des paliers usuels UE 5.x, l'installation n'a pas pu être lue lors de cette fiche). Écarts éventuels = ajustement de valeur uniquement.
|
||||
2. **Lecture en jeu / éditeur** (redémarrage de l'éditeur nécessaire après création du fichier) : pour `sg.ShadowQuality 0` puis `3`, lire `r.Shadow.MaxResolution` (512 → 2048) ; idem `sg.TextureQuality` → `r.Streaming.PoolSize` (200 → 1000), `sg.EffectsQuality` → `r.RefractionQuality`, `sg.AntiAliasingQuality` → `r.PostProcessAAQuality`.
|
||||
3. **Non-régression rendu :** à niveau 3, GI dynamique off, VSM off, TAA (méthode 2), reflets SSR — inchangés.
|
||||
4. **Pool de textures :** surveiller sur machines à VRAM faible (200 Mo à Low) la netteté et l'absence de « streaming pool over budget » ; ajuster `r.Streaming.PoolSize` si besoin.
|
||||
5. **Curseurs BP existants** (`ShadowQuality`, `PostProcessAAQuality`, `PostProcessQuality`) : toujours actifs et sans modification ; `PostProcessAAQuality` écrit directement `r.PostProcessAAQuality` (dernière écriture gagnante). Synchronisation/masquage = chantier `LPVideo*` ultérieur.
|
||||
6. **Effet de bord à surveiller :** `r.DetailMode=0` (Low) masque les composants en mode « detail » ; visuel de l'intérieur des chambres à vérifier.
|
||||
134
Docs/Migration/etat/VQ/VQ1/VQ1-SPEC.md
Normal file
134
Docs/Migration/etat/VQ/VQ1/VQ1-SPEC.md
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
# VQ1-SPEC — Spécification `DefaultScalability.ini` (Low → Epic)
|
||||
|
||||
**Fiche :** VQ1-T1
|
||||
**Rôle :** A-ARCH
|
||||
**Modèle attendu :** claude-sonnet-5-5-high (M2)
|
||||
**Date :** 07 octobre 2026
|
||||
**Workspace :** `d:\Game\LifePlay-master 5.5`
|
||||
**Statut :** Spécification — prête pour implémentation (VQ1-T2)
|
||||
**Dépend de :** `VQ0-CART.md` (cartographie des réglages Video), lot L7c (`Config/DefaultEngine.ini`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Objectif
|
||||
|
||||
Définir, pour le projet, un fichier `Config/DefaultScalability.ini` qui précise explicitement les cvars appliqués par les niveaux de qualité **0 (Low) → 3 (Epic)** pour **quatre groupes seulement** :
|
||||
|
||||
| Groupe | cvar de niveau | Section INI (par niveau) |
|
||||
|---|---|---|
|
||||
| Ombres | `sg.ShadowQuality` | `[ShadowQuality@0]` … `[ShadowQuality@3]` |
|
||||
| Textures | `sg.TextureQuality` | `[TextureQuality@0]` … `[TextureQuality@3]` |
|
||||
| Effets | `sg.EffectsQuality` | `[EffectsQuality@0]` … `[EffectsQuality@3]` |
|
||||
| Anti-aliasing (qualité) | `sg.AntiAliasingQuality` | `[AntiAliasingQuality@0]` … `[AntiAliasingQuality@3]` |
|
||||
|
||||
Ces quatre groupes préparent le futur bloc `LPVideo*` (VQ0-CART §7) et remplacent la dépendance implicite aux valeurs par défaut du moteur par des valeurs **lisibles, versionnées et revues**.
|
||||
|
||||
## 2. Périmètre et interdits
|
||||
|
||||
### 2.1 Dans le périmètre
|
||||
|
||||
- Niveaux **0, 1, 2, 3** des 4 groupes ci-dessus.
|
||||
- Cvars de qualité « classiques » UE5 (résolution d'ombres, cascades, pool de textures, anisotropie, qualité de réfraction, qualité du post-AA).
|
||||
|
||||
### 2.2 Interdits (hors périmètre — ne doit jamais apparaître dans `DefaultScalability.ini`)
|
||||
|
||||
| Interdit | Raison |
|
||||
|---|---|
|
||||
| Illumination globale dynamique (`r.DynamicGlobalIlluminationMethod`, `r.Lumen.*`, groupe `GlobalIlluminationQuality`) | Verrouillée à `0` par le lot L7c (rendu 4.27 conservé). |
|
||||
| Ombres virtuelles / VSM (`r.Shadow.Virtual.*`, y compris `r.Shadow.Virtual.Enable`) | Verrouillées à `0` par L7c. Aucune cvar `r.Shadow.Virtual.*` ne doit être ajoutée, même « inoffensive ». |
|
||||
| Méthode d'anti-aliasing (`r.AntiAliasingMethod`) | Verrouillée à `2` (TAA) par le lot L6a / L7c : TSR fait scintiller les maillages à morphs. Le groupe `AntiAliasingQuality` ne règle **que** la qualité, pas la méthode. |
|
||||
| Méthode de reflets (`r.ReflectionMethod`, `r.SSR.*`, groupe `ReflectionQuality`) | Verrouillée à `2` (SSR) par L7c. |
|
||||
| Groupes `ViewDistanceQuality`, `PostProcessQuality`, `ShadingQuality`, `FoliageQuality`, `LandscapeQuality`, `ResolutionQuality`, `GlobalIlluminationQuality`, `ReflectionQuality`, `SkeletalMeshQuality`, `HeightFieldShadowQuality`… | Non demandés ; les valeurs moteur (`BaseScalability.ini`) restent inchangées. |
|
||||
| Niveau Cinematic (`@Cine`, niveau 4) | Non exposé au joueur ; valeurs moteur conservées. |
|
||||
| Toute modification de `Config/DefaultEngine.ini` | Les contraintes L7c/L6a y restent la source de vérité. |
|
||||
|
||||
> **Garde-fou L7c (rappel) :** `r.DynamicGlobalIlluminationMethod=0`, `r.ReflectionMethod=2`, `r.Shadow.Virtual.Enable=0`, `r.AntiAliasingMethod=2` dans `[/Script/Engine.RendererSettings]` de `DefaultEngine.ini`. Ces valeurs sont au niveau de priorité *ProjectSetting*, supérieur à *Scalability* : elles ne peuvent pas être écrasées par un niveau de scalabilité, mais la règle ci-dessus évite néanmoins toute tentative.
|
||||
|
||||
## 3. Correspondance des niveaux
|
||||
|
||||
| Niveau | Nom | Usage |
|
||||
|---|---|---|
|
||||
| 0 | Low | Machines faibles / GPU intégrés |
|
||||
| 1 | Medium | Machines modestes |
|
||||
| 2 | High | Cible standard |
|
||||
| 3 | Epic | Qualité maximale proposée ; reste **conservatrice** (pas de valeur au-delà du palier Epic moteur) |
|
||||
|
||||
Principe « conservateur » : chaque valeur est choisie **au plus égale** à l'équivalent Epic du moteur UE 5.5 et sans activer de fonctionnalité nouvelle par rapport au rendu 4.27 validé. Aucune cvar susceptible de changer le *look* (volumetric fog, distance-field shadows, capsule shadows, SSS profile, bloom…) n'est incluse : elles restent aux valeurs moteur.
|
||||
|
||||
## 4. Tableau niveau → cvars
|
||||
|
||||
### 4.1 ShadowQuality (`sg.ShadowQuality`)
|
||||
|
||||
| cvar | 0 Low | 1 Medium | 2 High | 3 Epic | Commentaire |
|
||||
|---|---|---|---|---|---|
|
||||
| `r.Shadow.CSM.MaxCascades` | 1 | 2 | 3 | 4 | Cascades du soleil |
|
||||
| `r.Shadow.MaxResolution` | 512 | 1024 | 2048 | 2048 | Résolution max ombre dynamique (plafonnée à 2048) |
|
||||
| `r.Shadow.MaxCSMResolution` | 512 | 1024 | 2048 | 2048 | Résolution max par cascade |
|
||||
| `r.Shadow.RadiusThreshold` | 0.06 | 0.05 | 0.04 | 0.03 | Rayon écran min. pour projeter une ombre |
|
||||
| `r.Shadow.DistanceScale` | 0.6 | 0.7 | 0.85 | 1.0 | Distance de rendu des ombres |
|
||||
| `r.Shadow.CSM.TransitionScale` | 0.0 | 0.25 | 0.6 | 1.0 | Fondu entre cascades |
|
||||
| `r.Shadow.PreShadowResolutionFactor` | 0.5 | 0.5 | 0.75 | 1.0 | Résolution des pré-ombres |
|
||||
|
||||
Non inclus volontairement : `r.Shadow.Virtual.*` (interdit), `r.DistanceFieldShadowing`, `r.CapsuleShadows`, `r.VolumetricFog*`, `r.LightMaxDrawDistanceScale`, `r.LightFunctionQuality`.
|
||||
Cohérence : `r.MinScreenRadiusForCSMDepth=0.010000` reste fixé dans `DefaultEngine.ini` (non touché).
|
||||
|
||||
### 4.2 TextureQuality (`sg.TextureQuality`)
|
||||
|
||||
| cvar | 0 Low | 1 Medium | 2 High | 3 Epic | Commentaire |
|
||||
|---|---|---|---|---|---|
|
||||
| `r.Streaming.MipBias` | 2.5 | 1 | 0 | 0 | Mips abaissés (0 = pleine résolution) |
|
||||
| `r.MaxAnisotropy` | 0 | 2 | 4 | 8 | Filtrage anisotrope |
|
||||
| `r.Streaming.PoolSize` | 200 | 400 | 700 | 1000 | Budget pool de streaming (Mo) |
|
||||
| `r.Streaming.MaxEffectiveScreenSize` | 0 | 0 | 0 | 0 | 0 = comportement moteur |
|
||||
|
||||
`r.TextureStreaming=True` reste imposé par `DefaultEngine.ini`.
|
||||
|
||||
### 4.3 EffectsQuality (`sg.EffectsQuality`)
|
||||
|
||||
| cvar | 0 Low | 1 Medium | 2 High | 3 Epic | Commentaire |
|
||||
|---|---|---|---|---|---|
|
||||
| `r.TranslucencyLightingVolumeDim` | 24 | 32 | 48 | 64 | Volume d'éclairage des translucides |
|
||||
| `r.RefractionQuality` | 0 | 2 | 2 | 3 | Qualité de réfraction |
|
||||
| `r.SSS.Scale` | 0.25 | 0.5 | 0.75 | 1.0 | Échelle subsurface scattering |
|
||||
| `r.SSS.SampleSet` | 0 | 1 | 2 | 2 | Jeu d'échantillons SSS |
|
||||
| `r.DetailMode` | 0 | 1 | 2 | 2 | Seuil des composants « detail mode » |
|
||||
| `fx.Niagara.QualityLevel` | 0 | 1 | 2 | 3 | Niveau de qualité Niagara |
|
||||
|
||||
Non inclus : `r.SSR.Quality`, `r.Lumen.*` (interdits), `r.BloomQuality`, `r.SceneColorFormat`, `r.EmitterSpawnRateScale`.
|
||||
|
||||
### 4.4 AntiAliasingQuality (`sg.AntiAliasingQuality`)
|
||||
|
||||
| cvar | 0 Low | 1 Medium | 2 High | 3 Epic | Commentaire |
|
||||
|---|---|---|---|---|---|
|
||||
| `r.PostProcessAAQuality` | 0 | 2 | 4 | 4 | Qualité du post-AA (échelle moteur 0–6) |
|
||||
|
||||
Non inclus : `r.AntiAliasingMethod` (interdit, fixé à 2/TAA), `r.TSR.*` (TSR non utilisé), `r.TemporalAA*`.
|
||||
`r.PostProcessAAQuality=4` pour Epic = palier moteur ; la valeur 6 est réservée à Cinematic et n'est pas utilisée.
|
||||
|
||||
## 5. Note « Overall »
|
||||
|
||||
`sg.OverallQuality` (« Overall ») n'a **pas** de section propre dans `DefaultScalability.ini` : l'application d'un niveau Overall `N` consiste à **appliquer le même niveau `N` aux quatre groupes** (`sg.ShadowQuality`, `sg.TextureQuality`, `sg.EffectsQuality`, `sg.AntiAliasingQuality` = `N`). Les autres groupes de scalabilité restent à leur valeur courante.
|
||||
|
||||
Conséquence pour le futur sous-système `LPVideo*` : un sélecteur « Overall » doit écrire `N` sur ces 4 groupes uniquement (via `Scalability::SetQualityLevels` en préservant les autres champs de `FQualityLevels`), et ne pas déclencher d'effet de bord sur GI, reflets, VSM ou méthode d'AA.
|
||||
|
||||
## 6. Règles de fusion et de priorité
|
||||
|
||||
1. `Config/DefaultScalability.ini` est lu après `Engine/Config/BaseScalability.ini` ; les sections portant le même nom (`[ShadowQuality@2]`…) sont **fusionnées clé par clé** : les clés non listées conservent la valeur moteur.
|
||||
2. Les cvars appliquées par scalabilité ont la priorité `SetByScalability`, **inférieure** à `SetByProjectSetting` (`DefaultEngine.ini`). Les verrous L7c ne peuvent donc pas être contournés.
|
||||
3. Les valeurs `sg.*` choisies par le joueur sont persistées dans `GameUserSettings.ini` (`Saved/Config/Windows`) ; elles choisissent le niveau, pas le contenu des sections.
|
||||
4. Aucun `DeviceProfile` n'est défini dans `Config/` (pas de `DefaultDeviceProfiles.ini`) : pas de surcharge supplémentaire à prévoir.
|
||||
5. Le fichier ne contient que des sections `@0`–`@3` des 4 groupes, rien d'autre.
|
||||
|
||||
## 7. Points d'attention (VQ0-CART §6)
|
||||
|
||||
- **Redondance BP :** les curseurs BP `ShadowQuality`, `PostProcessAAQuality` (qui pilote `r.PostProcessAAQuality` directement), `PostProcessQuality` restent actifs. Un curseur BP `AA Quality` qui fixe directement `r.PostProcessAAQuality` aura priorité de dernière écriture sur le niveau `sg.AntiAliasingQuality` ; le futur `LPVideo*` devra les masquer/synchroniser (hors périmètre VQ1).
|
||||
- **Aucun changement C++/Blueprint** dans VQ1 : uniquement configuration.
|
||||
- **Vérification des valeurs moteur :** les valeurs de §4 sont fondées sur les paliers usuels de `BaseScalability.ini` UE 5.x ; elles doivent être confrontées au fichier de l'installation 5.5 lors de la validation (VQ1-VAL).
|
||||
|
||||
## 8. Critères d'acceptation (pour VQ1-T2 et validation)
|
||||
|
||||
- [ ] `Config/DefaultScalability.ini` existe et ne contient que `[ShadowQuality@0..3]`, `[TextureQuality@0..3]`, `[EffectsQuality@0..3]`, `[AntiAliasingQuality@0..3]`.
|
||||
- [ ] Aucune occurrence de `Virtual`, `Lumen`, `GlobalIllumination`, `AntiAliasingMethod`, `ReflectionMethod`, `SSR` dans le fichier.
|
||||
- [ ] `Config/DefaultEngine.ini` inchangé (L7c intact).
|
||||
- [ ] En jeu, `sg.ShadowQuality 0..3` (etc.) modifie `r.Shadow.MaxResolution`, `r.Streaming.PoolSize`, etc. conformément au §4 (`Scalability` console / `stat`/`r.X` en lecture).
|
||||
- [ ] Rendu à niveau 3 : GI dynamique, VSM et méthode d'AA inchangés (Lumen/VSM off, TAA).
|
||||
56
Docs/Migration/etat/VQ/VQ2/VQ2-IMPL-H.md
Normal file
56
Docs/Migration/etat/VQ/VQ2/VQ2-IMPL-H.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# VQ2-IMPL-H — Implémentation `ULPVideoQualitySubsystem`
|
||||
|
||||
**Fiches :** VQ2-T2 (+ VQ2-T3 `CategoryOf`)
|
||||
**Rôle :** A-IMPL-H
|
||||
**Date :** 07 octobre 2026
|
||||
**Spec :** `VQ2-SPEC.md`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Prévu | `claude-fable-5-1-thinking-high` (bloqué par policy) |
|
||||
| Attendu (consigne) | `claude-sonnet-5-5-high` (M2+, repli M3) |
|
||||
| **Réel** | `claude-sonnet-5-5-high` (Claude Sonnet 5.5) |
|
||||
|
||||
## Fichiers
|
||||
|
||||
| Fichier | Action |
|
||||
|---|---|
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.h` | Nouveau : `FLPVideoLevels`, `ULPVideoQualitySubsystem` |
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.cpp` | Nouveau : SetLevels, RefreshWidgets, Load, Save, EnsureControls, Tick, ApplyToScalability |
|
||||
| `Source/LifePlay/LPSettingsSubsystem.cpp` | `CategoryOf` + `ApplyRows` (T3) |
|
||||
|
||||
`LifePlay.Build.cs` et `LPColorGradeSubsystem.*` inchangés.
|
||||
|
||||
## Réalisé
|
||||
|
||||
- Widgets `LPVideoOverall`, `LPVideoShadows`, `LPVideoTextures`, `LPVideoEffects`, `LPVideoAA`, `LPVideoRecommended`, insérés via `InsertChildAt` avant le premier `LPColor*` (repli `AddChild` + réordonnancement des `LPColor*`).
|
||||
- `ApplyToScalability()` : `Scalability::GetQualityLevels()` puis écriture de 4 champs seulement (`ShadowQuality`, `TextureQuality`, `EffectsQuality`, `AntiAliasingQuality`), `SetQualityLevels(Q, true)`, relecture et repli `sg.*` en `ECVF_SetByConsole` (R1).
|
||||
- Persistance `Saved/Config/Windows/LPVideoQuality.ini` (`Version=1`, `Overall`, `Shadows`, `Textures`, `Effects`, `AA`) ; `Overall` recalculé par `Normalize()` au `Load()`.
|
||||
- Recommended : stub = niveau 2 (High), log `Video : Recommended = stub (High) ; VRAM prevue en VQ3`.
|
||||
- `EnsureControls()` met aussi `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` en `Collapsed`.
|
||||
- T3 `LPSettingsSubsystem.cpp` :
|
||||
- `CategoryOf` : `LPVideo*` → `TabVideo` ; `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` → `TabHidden` (-2) ; `BackButton` → `TabAlways` (-1).
|
||||
- `ApplyRows` : `bShow = Category == TabAlways || (Category != TabHidden && ActiveTab != TabKeyboard && Category == ActiveTab)` ; -1 toujours visible, -2 toujours replié.
|
||||
- Grep : `CategoryOf` n'a qu'un seul appelant (`ApplyRows`).
|
||||
|
||||
## Écarts / choix par rapport à la spec
|
||||
|
||||
- **Drapeau `bConfigured`** : sans INI et sans action du joueur, `ApplyToScalability()` ne fait rien (état moteur préservé, §6.2 « aucun changement »). Les niveaux affichés sont alors relus depuis Scalability à chaque injection du menu. Dès qu'un INI existe ou que `SetLevels` est appelé, les déclencheurs a/b/c réappliquent les niveaux.
|
||||
- Pas de `Save()` à l'initialisation.
|
||||
- `AddRow` : signature adaptée (`ERow`, widget de ligne en sortie) car l'insertion se fait après création des 6 lignes.
|
||||
- Pas de delegate `OnControlsInjected` (Q2) : `ULPSettingsSubsystem::Tick` rappelle `ApplyRows()` quand `GetChildrenCount()` change (à confirmer en VQ2-VAL).
|
||||
|
||||
## Build
|
||||
|
||||
Commande :
|
||||
|
||||
```
|
||||
D:\Game\Launcher\Engine\UE_5.5\Engine\Build\Build.bat -> Engine\Build\BatchFiles\Build.bat
|
||||
LifePlayEditor Win64 Development -Project="D:\Game\LifePlay-master 5.5\LifePlay.uproject"
|
||||
```
|
||||
|
||||
Résultat : **compile OK** (UHT sans erreur, `UnrealEditor-LifePlay.dll` relié, exit code 0, aucun warning/erreur sur les fichiers VQ2).
|
||||
|
||||
Non testé en exécution (VQ2-VAL).
|
||||
336
Docs/Migration/etat/VQ/VQ2/VQ2-SPEC.md
Normal file
336
Docs/Migration/etat/VQ/VQ2/VQ2-SPEC.md
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
# VQ2-SPEC — Spécification `ULPVideoQualitySubsystem`
|
||||
|
||||
**Fiche :** VQ2-T1
|
||||
**Rôle :** A-ARCH
|
||||
**Modèle attendu :** claude-sonnet-5-5-high (M2)
|
||||
**Date :** 07 octobre 2026
|
||||
**Workspace :** `d:\Game\LifePlay-master 5.5`
|
||||
**Statut :** Spécification — prête pour implémentation (VQ2-T2) et branchement onglet (VQ2-T3)
|
||||
**Dépend de :** `VQ0-CART.md` (§5–§7), `VQ1-SPEC.md` (§4–§6, `Config/DefaultScalability.ini`)
|
||||
**Patron :** `Source/LifePlay/LPColorGradeSubsystem.h/.cpp` (CHG-161 / CHG-163)
|
||||
|
||||
---
|
||||
|
||||
## 1. Objectif
|
||||
|
||||
Ajouter, dans l'onglet **Video** de `SettingsScrollBox`, un bloc de six lignes C++ (`LPVideo*`) qui pilote les **4 groupes Scalability** définis en VQ1 (ombres, textures, effets, anti-aliasing) par un curseur **Overall** (préréglage Low → Epic) et quatre curseurs individuels. Le bloc est inséré **avant** `LPColorBrightness` (VQ0-CART §7).
|
||||
|
||||
Hors périmètre VQ2 : calcul VRAM du bouton Recommended (VQ3), tout changement de `DefaultScalability.ini` / `DefaultEngine.ini`, tout changement Blueprint, GI / VSM / méthode d'AA / reflets (verrous L7c, VQ1 §2.2).
|
||||
|
||||
## 2. Fichiers
|
||||
|
||||
| Fichier | Action | Fiche |
|
||||
|---|---|---|
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.h` | Nouveau | VQ2-T2 |
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.cpp` | Nouveau | VQ2-T2 |
|
||||
| `Source/LifePlay/LPSettingsSubsystem.cpp` (`CategoryOf`, `ApplyRows`) | Modifié (§9) | VQ2-T3 |
|
||||
| `Source/LifePlay/LifePlay.Build.cs` | **Inchangé** : `Engine` (Scalability), `Slate`, `SlateCore`, `UMG` déjà présents | — |
|
||||
| `Saved/Config/Windows/LPVideoQuality.ini` | Créé à l'exécution | — |
|
||||
|
||||
Includes attendus (.cpp) : `Scalability.h`, `Components/Button.h`, `Components/ScrollBox.h`, `Components/ScrollBoxSlot.h`, `Components/HorizontalBox.h`, `Components/HorizontalBoxSlot.h`, `Components/SizeBox.h`, `Components/Slider.h`, `Components/TextBlock.h`, `Blueprint/UserWidget.h`, `HAL/IConsoleManager.h`, `Misc/ConfigCacheIni.h`, `Misc/Paths.h`, `LifePlay.h` (`LogLifePlay`).
|
||||
|
||||
## 3. Types
|
||||
|
||||
### 3.1 `FLPVideoLevels`
|
||||
|
||||
```cpp
|
||||
USTRUCT()
|
||||
struct FLPVideoLevels
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY() int32 Overall = 2; // 0..3, ou -1 = Custom
|
||||
UPROPERTY() int32 Shadows = 2; // 0..3 -> sg.ShadowQuality
|
||||
UPROPERTY() int32 Textures = 2; // 0..3 -> sg.TextureQuality
|
||||
UPROPERTY() int32 Effects = 2; // 0..3 -> sg.EffectsQuality
|
||||
UPROPERTY() int32 AA = 2; // 0..3 -> sg.AntiAliasingQuality
|
||||
|
||||
static FLPVideoLevels FromOverall(int32 Level); // 4 groupes = Level (clampé 0..3), Overall = Level
|
||||
void Normalize(); // clamp 0..3 des 4 groupes ; Overall := (4 groupes égaux ? valeur : -1)
|
||||
bool operator==(const FLPVideoLevels& Other) const;
|
||||
};
|
||||
```
|
||||
|
||||
Les valeurs par défaut du struct ne servent que de repli ; la première exécution dérive les niveaux de l'état Scalability courant (§6.2).
|
||||
|
||||
### 3.2 Invariant
|
||||
|
||||
Après `Normalize()` : `Overall == -1` **si et seulement si** les 4 groupes ne sont pas tous égaux. Si `Overall >= 0`, les 4 groupes valent `Overall`. L'invariant est ré-imposé à chaque `Load()` : une valeur `Overall` incohérente lue dans l'INI est ignorée et recalculée.
|
||||
|
||||
### 3.3 Libellés de niveaux
|
||||
|
||||
| Niveau | Libellé (readout) |
|
||||
|---|---|
|
||||
| 0 | `Low` |
|
||||
| 1 | `Med` |
|
||||
| 2 | `High` |
|
||||
| 3 | `Epic` |
|
||||
| Overall = -1 | `Custom` |
|
||||
|
||||
## 4. Classe
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class LIFEPLAY_API ULPVideoQualitySubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
// Point d'entrée unique de modification (UI, Recommended, VQ3).
|
||||
void SetLevels(FLPVideoLevels NewLevels, bool bFromOverall);
|
||||
FLPVideoLevels GetLevels() const { return Levels; }
|
||||
void RefreshWidgets();
|
||||
|
||||
UFUNCTION() void OnOverallChanged(float Value);
|
||||
UFUNCTION() void OnShadowsChanged(float Value);
|
||||
UFUNCTION() void OnTexturesChanged(float Value);
|
||||
UFUNCTION() void OnEffectsChanged(float Value);
|
||||
UFUNCTION() void OnAAChanged(float Value);
|
||||
UFUNCTION() void OnRecommendedClicked();
|
||||
|
||||
private:
|
||||
enum ERow { Overall, Shadows, Textures, Effects, AA, NumRows };
|
||||
struct FRowRefs { TWeakObjectPtr<USlider> Slider; TWeakObjectPtr<UTextBlock> Readout; };
|
||||
|
||||
bool Tick(float DeltaTime); // 0.5 s, comme ColorGrade
|
||||
void EnsureControls();
|
||||
void Load();
|
||||
void Save() const;
|
||||
void ApplyToScalability(); // §6
|
||||
USlider* AddRow(...); // même signature que ColorGrade::AddRow (+ ERow)
|
||||
UButton* AddButtonRow(...); // ligne Recommended
|
||||
static FString LevelLabel(int32 Level);
|
||||
|
||||
FTSTicker::FDelegateHandle TickHandle;
|
||||
FRowRefs Rows[NumRows];
|
||||
TWeakObjectPtr<UButton> RecommendedButton;
|
||||
TWeakObjectPtr<UWorld> LastAppliedWorld;
|
||||
FLPVideoLevels Levels;
|
||||
bool bRefreshing = false; // garde anti-réentrance
|
||||
};
|
||||
```
|
||||
|
||||
`Initialize` : `Load()` → `ApplyToScalability()` → enregistre le ticker 0.5 s (même patron que `ULPColorGradeSubsystem`). `Deinitialize` : retire le ticker.
|
||||
|
||||
## 5. API
|
||||
|
||||
### 5.1 `SetLevels(FLPVideoLevels NewLevels, bool bFromOverall)`
|
||||
|
||||
Ordre strict :
|
||||
|
||||
1. **Normaliser.**
|
||||
- `bFromOverall == true` : `Overall` clampé 0..3 → `NewLevels = FromOverall(Overall)`. Si `Overall < 0`, la valeur est ignorée et on retombe sur le cas `false` (pas de fan-out).
|
||||
- `bFromOverall == false` : clamp des 4 groupes ; `Overall` recalculé par `Normalize()` (égaux → valeur, sinon `-1`). Le champ `Overall` fourni est **ignoré**.
|
||||
2. `Levels = NewLevels`.
|
||||
3. **`ApplyToScalability()`** (§6).
|
||||
4. **`RefreshWidgets()`** (§5.2).
|
||||
5. **`Save()`** (§7).
|
||||
|
||||
Aucune étape ne doit dépendre de la présence des widgets : `SetLevels` est appelable menu fermé (Initialize, VQ3, console de debug).
|
||||
|
||||
### 5.2 `RefreshWidgets()`
|
||||
|
||||
Garde `bRefreshing` (set/reset autour du corps). Pour chaque `FRowRefs` valide :
|
||||
|
||||
| Ligne | Position du curseur | Readout |
|
||||
|---|---|---|
|
||||
| Overall | `Overall >= 0` ? `Overall` : `clamp(round(moyenne des 4 groupes), 0, 3)` | `LevelLabel(Overall)` ou **`Custom`** |
|
||||
| Shadows / Textures / Effects / AA | valeur du groupe | `LevelLabel(valeur)` |
|
||||
|
||||
`USlider::SetValue` ne diffuse pas `OnValueChanged` (seul l'événement Slate utilisateur le fait) ; la garde `bRefreshing` protège néanmoins contre un changement de comportement. Les handlers `On*Changed` retournent immédiatement si `bRefreshing`.
|
||||
|
||||
### 5.3 Handlers
|
||||
|
||||
| Handler | Action |
|
||||
|---|---|
|
||||
| `OnOverallChanged(V)` | `Overall = clamp(RoundToInt(V),0,3)` → `SetLevels(FromOverall(Overall), true)` |
|
||||
| `OnShadowsChanged(V)` | `L = Levels; L.Shadows = clamp(RoundToInt(V),0,3)` → `SetLevels(L, false)` |
|
||||
| `OnTexturesChanged` / `OnEffectsChanged` / `OnAAChanged` | idem sur leur champ |
|
||||
| `OnRecommendedClicked()` | Stub VQ2 : `SetLevels(FromOverall(2), true)` + `UE_LOG(... "Video : Recommended = stub (High) ; VRAM prevue en VQ3")`. VQ3 remplace uniquement le corps de cette fonction. |
|
||||
|
||||
### 5.4 `Load()` / `Save()` — voir §7.
|
||||
|
||||
### 5.5 `EnsureControls()` — voir §8.
|
||||
|
||||
## 6. Application Scalability
|
||||
|
||||
### 6.1 `ApplyToScalability()`
|
||||
|
||||
```cpp
|
||||
Scalability::FQualityLevels Q = Scalability::GetQualityLevels(); // préserve les champs non gérés
|
||||
Q.ShadowQuality = Levels.Shadows;
|
||||
Q.TextureQuality = Levels.Textures;
|
||||
Q.EffectsQuality = Levels.Effects;
|
||||
Q.AntiAliasingQuality = Levels.AA;
|
||||
Scalability::SetQualityLevels(Q, /*bForce=*/true);
|
||||
```
|
||||
|
||||
Règles :
|
||||
|
||||
- **Seuls 4 champs sont écrits** (VQ1 §5). `ResolutionQuality`, `ViewDistanceQuality`, `PostProcessQuality`, `GlobalIlluminationQuality`, `ReflectionQuality`, `ShadingQuality`, `FoliageQuality`, etc. restent ceux de l'état courant. Aucun appel à `SetFromSingleQualityLevel`.
|
||||
- Niveau 4 (Cinematic) non exposé : pas de valeur > 3 écrite.
|
||||
- Pas d'appel à `Scalability::SaveState(GGameUserSettingsIni)` : la source de vérité est `LPVideoQuality.ini` (§7). `GameUserSettings.ini` peut contenir des `sg.*` périmés ; ils sont appliqués par le moteur **avant** `Initialize`, puis écrasés par `ApplyToScalability()`.
|
||||
- **Vérification de relecture** (log `Display`, une ligne) : après `SetQualityLevels`, relire `Scalability::GetQualityLevels()` et comparer aux 4 niveaux ; en cas d'écart, `UE_LOG(Warning)` + repli (R1, §11) : pour chaque groupe divergent, `IConsoleManager::Get().FindConsoleVariable(TEXT("sg.<Groupe>"))->Set(Level, ECVF_SetByConsole)` puis un second `SetQualityLevels`.
|
||||
|
||||
### 6.2 Premier lancement (pas d'INI)
|
||||
|
||||
Si `LPVideoQuality.ini` n'existe pas : `Levels` = niveaux 4 groupes lus dans `Scalability::GetQualityLevels()` (clampés 0..3 ; un niveau 4/Cine est ramené à 3), puis `Normalize()` ; **aucun `Save()` à l'initialisation** (le fichier n'est écrit qu'au premier changement de l'utilisateur). Ainsi un joueur qui n'ouvre jamais l'onglet conserve exactement son comportement actuel.
|
||||
|
||||
### 6.3 Déclencheurs de ré-application (idempotents)
|
||||
|
||||
| # | Déclencheur | Raison |
|
||||
|---|---|---|
|
||||
| a | `Initialize` | Appliquer l'INI sur l'état moteur chargé |
|
||||
| b | Premier `Tick` après changement de monde de jeu (`LastAppliedWorld != GetWorld()` et `IsGameWorld()`) | Le Blueprint `MainMenu` / `GameSettings` peut réappliquer ses valeurs `.sav` au chargement d'un niveau |
|
||||
| c | À chaque injection réussie de `EnsureControls()` (nouvelle instance `MainMenu`) | Idem, à l'ouverture du menu |
|
||||
|
||||
Pas de ré-application continue à 0.5 s : cela écraserait les commandes console `sg.*` de debug.
|
||||
|
||||
## 7. Persistance — `Saved/Config/Windows/LPVideoQuality.ini`
|
||||
|
||||
Chemin : `FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config/Windows/LPVideoQuality.ini"))` (patron `LPColorGrade::IniPath`). Lecture/écriture via `FConfigFile` (`Read` → `SetString` → `Write`).
|
||||
|
||||
```ini
|
||||
[LPVideoQuality]
|
||||
Version=1
|
||||
Overall=2
|
||||
Shadows=2
|
||||
Textures=2
|
||||
Effects=2
|
||||
AA=2
|
||||
```
|
||||
|
||||
- `Load()` : lit les 5 clés (`FCString::Atoi`), clamp 0..3 (`Overall` : -1..3), **`Normalize()` recalcule `Overall`**. Une clé manquante conserve la valeur dérivée du §6.2 ; si **aucune** clé n'est présente, comportement §6.2. Log `Display` en français, style ColorGrade : `Video : relu Overall %d, Shadows %d, Textures %d, Effects %d, AA %d depuis %s`.
|
||||
- `Save()` : écrit les 6 clés + log `... enregistres dans %s`. Appelé uniquement par `SetLevels`.
|
||||
- `Version` réservé aux migrations futures ; une version > 1 est lue en mode best-effort (clés connues seulement).
|
||||
|
||||
## 8. `EnsureControls()` — injection UI
|
||||
|
||||
### 8.1 Recherche du `SettingsScrollBox`
|
||||
|
||||
Identique à `ULPColorGradeSubsystem::EnsureControls` : `TObjectIterator<UScrollBox>`, nom `SettingsScrollBox`, hors CDO/archétype, `GetTypedOuter<UUserWidget>()` valide, non CDO, `GetGameInstance() == GetGameInstance()`. Sortie silencieuse si introuvable.
|
||||
|
||||
### 8.2 Garde
|
||||
|
||||
Sortie si un enfant direct du scroll s'appelle `LPVideoOverall` (déjà injecté pour cette instance de menu). Une nouvelle instance `MainMenu` n'a pas ce widget → réinjection ; les `TWeakObjectPtr` précédents sont remplacés.
|
||||
|
||||
### 8.3 Lignes injectées (ordre d'affichage)
|
||||
|
||||
| # | Nom widget | Libellé | Type | Min–Max | Pas | Handler | Info-bulle |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 1 | `LPVideoOverall` | `Overall Quality` | Slider + readout | 0–3 | 1 | `OnOverallChanged` | `Preset applied to shadows, textures, effects and anti-aliasing. Custom when they differ.` |
|
||||
| 2 | `LPVideoShadows` | `Shadows` | Slider + readout | 0–3 | 1 | `OnShadowsChanged` | `Shadow resolution and distance.` |
|
||||
| 3 | `LPVideoTextures` | `Textures` | Slider + readout | 0–3 | 1 | `OnTexturesChanged` | `Texture filtering and streaming budget.` |
|
||||
| 4 | `LPVideoEffects` | `Effects` | Slider + readout | 0–3 | 1 | `OnEffectsChanged` | `Translucency, refraction, subsurface scattering and particles.` |
|
||||
| 5 | `LPVideoAA` | `Anti-Aliasing` | Slider + readout | 0–3 | 1 | `OnAAChanged` | `Anti-aliasing quality. The method itself is fixed.` |
|
||||
| 6 | `LPVideoRecommended` | `Recommended` | `UButton` + `UTextBlock` | — | — | `OnRecommendedClicked` | `Apply the recommended preset for this PC.` |
|
||||
|
||||
Les libellés contiennent les mots-clés `shadow` (ligne 2) / `quality` (ligne 1) ; sans importance, `CategoryOf` classe sur le préfixe `LPVideo` avant le texte (§9).
|
||||
|
||||
### 8.4 Structure d'une ligne
|
||||
|
||||
Copie du patron `ColorGrade::AddRow` : `UHorizontalBox` nommé (`NewObject<UHorizontalBox>(Scroll, Name)`) contenant `USizeBox` 220 px + `UTextBlock` (légende), `USlider` (Fill), `UTextBlock` (readout). Mêmes paddings (10/0/20/0, 0/0/20/0, 0/0/10/0 ; bas 10 px côté scroll). Style : police/couleur/`FSliderStyle` copiés d'une ligne existante via `FindLook` (repli `MenuFont()` : `Regular 20`, contour noir 2 px, opacité 0.95).
|
||||
|
||||
Particularités sliders : `SetMinValue(0)`, `SetMaxValue(3)`, `SetStepSize(1)`, `SetLocked(false)`. Branchement `OnValueChanged` via `FScriptDelegate::BindUFunction` (patron ColorGrade). La ligne Recommended : `UHorizontalBox` → `UButton` (`OnClicked` via `FScriptDelegate`) contenant un `UTextBlock` `Recommended`, même police/couleur, alignement gauche, padding 10 px.
|
||||
|
||||
Les helpers `FindLook` / `MenuFont` sont **dupliqués** dans un `namespace LPVideoQuality` local au `.cpp` (V1 : aucune modification de `LPColorGradeSubsystem`). Refactor commun éventuel hors périmètre.
|
||||
|
||||
### 8.5 Position d'insertion : avant le premier `LPColor*`
|
||||
|
||||
Algorithme :
|
||||
|
||||
1. `InsertAt = INDEX_NONE` ; parcourir les enfants directs du scroll, `InsertAt` = index du **premier** enfant dont `GetName().StartsWith(TEXT("LPColor"))`.
|
||||
2. Créer les 6 lignes (jamais ajoutées directement).
|
||||
3. Si `InsertAt != INDEX_NONE` : pour `i = 0..5`, `Scroll->InsertChildAt(InsertAt + i, Row[i])` (`UPanelWidget::InsertChildAt`) ; configurer ensuite le `UScrollBoxSlot` (padding bas 10) via `Cast<UScrollBoxSlot>(Row->Slot)`.
|
||||
4. Sinon (`LPColor*` pas encore injecté) : `Scroll->AddChild(Row[i])` dans l'ordre → les lignes seront suivies des `LPColor*` quand ColorGrade s'injectera (il ajoute en fin).
|
||||
5. **Repli** si `InsertChildAt` retourne `nullptr` : `AddChild` des 6 lignes, puis réordonner : collecter les enfants `LPColor*`, `RemoveChild` de chacun, et les `AddChild` de nouveau dans leur ordre d'origine (même objets ; `ColorGrade` garde ses `TWeakObjectPtr` valides).
|
||||
|
||||
Le garde de `ColorGrade::EnsureControls` (`LPColorBrightness` présent) n'est pas affecté par l'ordre. Les deux sous-systèmes sont indépendants (aucune référence croisée).
|
||||
|
||||
### 8.6 Masquage des widgets BP redondants
|
||||
|
||||
Widgets dirigés vers **Collapsed** : `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` (noms UMG, VQ0-CART §5). Conservés visibles : `ScreenPercentage`, `FullscreenButton`, `ShowFPS`, cases et SpinBoxes (VQ0-CART §8).
|
||||
|
||||
Deux niveaux de mise en œuvre :
|
||||
|
||||
1. **`EnsureControls()` (VQ2-T2)** : après injection, `SetVisibility(Collapsed)` sur ces trois enfants directs (recherche par `GetFName()`). Sert d'effet immédiat.
|
||||
2. **`LPSettingsSubsystem` (VQ2-T3)** : **obligatoire** pour la durabilité — `ApplyRows()` remet `Visible` toute ligne dont `Category == ActiveTab` à chaque changement d'onglet ; sans la modification du §9, le masquage du point 1 est annulé au premier clic d'onglet.
|
||||
|
||||
Conséquences V1 (pragmatique, documentées VQ0-CART §6.1/§6.2) :
|
||||
|
||||
- Le curseur `PP Quality (FPS intensive)` disparaît ; `sg.PostProcessQuality` n'est plus piloté par l'utilisateur et reste à sa dernière valeur / valeur moteur (VQ1 §2.2 : groupe hors périmètre). Voir question Q1.
|
||||
- Le curseur `AA Quality` BP (écriture directe de `r.PostProcessAAQuality`) est remplacé par `LPVideoAA` (`sg.AntiAliasingQuality`, VQ1 §4.4 : 0/2/4/4).
|
||||
- Aucune synchronisation BP ↔ LPVideo : les widgets masqués ne sont plus lus ni écrits par le joueur.
|
||||
|
||||
## 9. Intégration `LPSettingsSubsystem` (fiche VQ2-T3, rappel de contrat)
|
||||
|
||||
Dans `LPSettingsUi::CategoryOf`, **avant** `RowText` (même endroit que la règle `LPColor`) :
|
||||
|
||||
```cpp
|
||||
if (Widget && (Widget->GetName().StartsWith(TEXT("LPColor")) || Widget->GetName().StartsWith(TEXT("LPVideo"))))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
```
|
||||
|
||||
Et, pour les trois widgets BP masqués, une catégorie « toujours masquée » :
|
||||
|
||||
```cpp
|
||||
constexpr int32 TabHidden = -2;
|
||||
// dans CategoryOf, après BackButton :
|
||||
if (Widget && (Widget->GetFName() == TEXT("ShadowQuality")
|
||||
|| Widget->GetFName() == TEXT("PostProcessQuality")
|
||||
|| Widget->GetFName() == TEXT("PostProcessAAQuality")))
|
||||
{
|
||||
return TabHidden;
|
||||
}
|
||||
```
|
||||
|
||||
`ApplyRows()` : `Category == -1` reste « toujours visible » (`BackButton`) ; `Category == TabHidden` → `Collapsed` quel que soit l'onglet. **Attention :** l'actuel `bShow = Category < 0 || ...` rendrait `-2` visible ; la condition doit devenir `Category == -1 || (...)`. Aucun autre appelant de `CategoryOf` ne doit traiter `< 0` comme « visible » (à vérifier par Grep en T3).
|
||||
|
||||
Ordre dans le `ScrollBox` : `ApplyRows()` ne change que la visibilité, pas l'ordre ; l'ordre « BP, LPVideo*, LPColor* » est donc stable. Après injection tardive de `LPVideo*`, la visibilité initiale des nouvelles lignes est celle de `SWidget` par défaut (visible) tant que `ApplyRows()` n'a pas été rappelé : **T3 doit déclencher `ApplyRows()` après injection** (ou `LPVideo` ne peut pas l'appeler : voir Q2).
|
||||
|
||||
## 10. Journalisation
|
||||
|
||||
Style ColorGrade, `LogLifePlay`, `Display`, français sans accents dans les littéraux (cohérence existante) :
|
||||
|
||||
- `Video : relu ... depuis <ini>` (Load)
|
||||
- `Video : Overall %d, Shadows %d, Textures %d, Effects %d, AA %d enregistres dans <ini>` (Save)
|
||||
- `Video : qualite appliquee (sg.ShadowQuality %d, sg.TextureQuality %d, sg.EffectsQuality %d, sg.AntiAliasingQuality %d)` (Apply)
|
||||
- `Video : controles ajoutes avant les reglages de couleur` (EnsureControls, une fois par injection)
|
||||
- `Warning` : écart de relecture Scalability (§6.1), `InsertChildAt` en repli (§8.5).
|
||||
|
||||
## 11. Risques
|
||||
|
||||
| ID | Risque | Mitigation |
|
||||
|---|---|---|
|
||||
| R1 | Le Blueprint (`SettingSlider`/`GameSettings`) a pu écrire `sg.*` ou `r.PostProcessAAQuality` avec une priorité **Console/Code**, supérieure à `SetByScalability` : `SetQualityLevels` serait alors ignoré silencieusement. | Relecture + repli `ECVF_SetByConsole` (§6.1) ; ré-application aux déclencheurs b/c (§6.3). À confirmer en VQ2-VAL (valeurs `r.Shadow.MaxResolution`, `r.Streaming.PoolSize`, `r.PostProcessAAQuality` conformes à VQ1 §4). |
|
||||
| R2 | `ApplyRows()` annule le masquage des widgets BP. | §9 obligatoire (T3). |
|
||||
| R3 | `InsertChildAt` indisponible/inopérant sur `UScrollBox`. | Repli §8.5.5. |
|
||||
| R4 | Perte de `PP Quality` : joueur ne peut plus baisser le coût post-process. | Décision V1 assumée ; voir Q1. |
|
||||
| R5 | `GameUserSettings.ini` contient des `sg.*` différents de `LPVideoQuality.ini`. | `LPVideoQuality.ini` est prioritaire dès qu'il existe (§6.1) ; sans INI, état moteur conservé (§6.2). |
|
||||
| R6 | Écart d'ordre d'initialisation ColorGrade/Video. | Insertion avant premier `LPColor*` si présent, sinon append ; les deux ordres donnent « Video avant Color » (§8.5). |
|
||||
| R7 | Interdits VQ1 (GI, VSM, méthode AA, reflets). | Seuls 4 champs de `FQualityLevels` écrits ; aucune cvar écrite directement hors repli R1 (`sg.*` uniquement). |
|
||||
|
||||
## 12. Questions ouvertes
|
||||
|
||||
| ID | Question | Proposition par défaut |
|
||||
|---|---|---|
|
||||
| Q1 | Faut-il lier `sg.PostProcessQuality` à `Effects` (ou `Overall`) pour ne pas figer un groupe devenu sans contrôle ? | **Non** en V1 (VQ1 §5 : 4 groupes seulement). À trancher avec A-VAL après mesure de coût. |
|
||||
| Q2 | Comment T3 est-il notifié d'une injection pour relancer `ApplyRows()` ? | `ULPSettingsSubsystem` a son propre ticker : `ApplyRows()` est déjà réexécuté après détection de nouvelles lignes (à vérifier en T3) ; sinon un delegate `FSimpleMulticastDelegate OnControlsInjected` exposé par `ULPVideoQualitySubsystem`. |
|
||||
| Q3 | Niveau recommandé du stub. | `High` (2) ; VQ3 le remplace par un calcul VRAM. |
|
||||
|
||||
## 13. Critères d'acceptation (VQ2-T2 / VQ2-T3 / VQ2-VAL)
|
||||
|
||||
- [ ] `LPVideoQualitySubsystem.h/.cpp` compilent sans toucher `LifePlay.Build.cs` ni `LPColorGradeSubsystem.*`.
|
||||
- [ ] Ordre dans l'onglet Video : lignes BP visibles → `LPVideoOverall`, `Shadows`, `Textures`, `Effects`, `AA`, `Recommended` → `LPColorBrightness` … `LPColorTint` (quel que soit l'ordre d'initialisation ColorGrade/Video).
|
||||
- [ ] `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` restent masqués après changement d'onglet aller-retour.
|
||||
- [ ] Overall 0→3 : les 4 curseurs suivent, readouts `Low/Med/High/Epic` ; `sg.ShadowQuality`, `sg.TextureQuality`, `sg.EffectsQuality`, `sg.AntiAliasingQuality` valent le niveau choisi (console `sg.X` en lecture) ; `sg.GlobalIlluminationQuality`, `sg.ReflectionQuality`, `sg.PostProcessQuality`, `sg.ViewDistanceQuality` inchangés.
|
||||
- [ ] Modifier un curseur individuel → Overall affiche `Custom` ; ramener les 4 au même niveau → Overall réaffiche ce niveau.
|
||||
- [ ] Redémarrage du jeu : niveaux restaurés depuis `Saved/Config/Windows/LPVideoQuality.ini` ; sans INI, aucun changement par rapport à l'état actuel.
|
||||
- [ ] Fermer/rouvrir le menu : lignes réinjectées avec les bonnes valeurs.
|
||||
- [ ] `Recommended` : applique High, ne plante pas, log stub.
|
||||
- [ ] Niveau 3 : GI dynamique, VSM et méthode d'AA inchangés (Lumen/VSM off, TAA) — rappel VQ1 §8.
|
||||
- [ ] Aucune occurrence de `Virtual`, `Lumen`, `AntiAliasingMethod`, `ReflectionMethod` dans le code ajouté.
|
||||
76
Docs/Migration/etat/VQ/VQ2/VQ2-VAL.md
Normal file
76
Docs/Migration/etat/VQ/VQ2/VQ2-VAL.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# VQ2-VAL — Validation `ULPVideoQualitySubsystem`
|
||||
|
||||
**Fiche :** VQ2-T4
|
||||
**Rôle :** A-VAL
|
||||
**Date :** 07 octobre 2026
|
||||
**Entrée :** `VQ2-IMPL-H.md` (VQ2-T2 + T3), `VQ2-SPEC.md`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Prévu / attendu | `claude-sonnet-5-5-high` (M2) |
|
||||
| **Utilisé** | `claude-sonnet-5-5-high` (Claude Sonnet 5.5) |
|
||||
|
||||
## Verdict : **PASS** (avec **BLOCKED-UI** sur le test manuel Settings)
|
||||
|
||||
## Preuves
|
||||
|
||||
### 1. Fichiers
|
||||
|
||||
| Vérification | Résultat |
|
||||
|---|---|
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.h` | présent (modifié 23:21) |
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.cpp` | présent (modifié 23:22) |
|
||||
| `UnrealEditor-LifePlay.dll` | 23:23:26, postérieur aux sources (VQ2-IMPL : compile OK, exit 0) |
|
||||
|
||||
### 2. `CategoryOf` (`LPSettingsSubsystem.cpp` l.140-154)
|
||||
|
||||
- `BackButton` → `TabAlways` : OK
|
||||
- `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` → `TabHidden` (`-2`, constante l.41) : OK
|
||||
- Nom commençant par `LPColor` ou `LPVideo` → `TabVideo` : OK
|
||||
- `ApplyRows` (l.599) : `bShow = Category == TabAlways || (Category != TabHidden && ActiveTab != TabKeyboard && Category == ActiveTab)` : OK
|
||||
- Le `Tick` de `ULPSettingsSubsystem` rappelle `ApplyRows()` quand `GetChildrenCount()` change (l.281-284) : la question Q2 d'IMPL-H est confirmée en statique.
|
||||
|
||||
### 3. Grep symboles (`Source/`)
|
||||
|
||||
| Symbole | Occurrences |
|
||||
|---|---|
|
||||
| `SetLevels` | `.h:59`, `.cpp:251` (définition), appels l.299, 310, 321, 332, 343, 350 |
|
||||
| `RefreshWidgets` | `.h:61`, `.cpp:268` (définition), appelé l.264 et l.572 |
|
||||
| `EnsureControls` | `.h:94`, `.cpp:431` (définition), appelé dans `Tick` l.140 |
|
||||
|
||||
### 4. Démarrage runtime (test à blanc)
|
||||
|
||||
Commande : `UnrealEditor-Cmd.exe LifePlay.uproject -game -nullrhi -nosound -unattended -nosplash -ExecCmds="quit"`, exit code 0, environ 77 s.
|
||||
Journal : `Saved/Logs/VQ2VAL.log`.
|
||||
|
||||
```
|
||||
LogLifePlay: Display: Video : pas de .../Saved/Config/Windows/LPVideoQuality.ini, niveaux lus depuis Scalability (Overall 3, Shadows 3, Textures 3, Effects 3, AA 3)
|
||||
```
|
||||
|
||||
- `Initialize` → `Load()` s'exécute et logue la lecture depuis Scalability. L'abonnement au ticker n'a pas fait crasher le démarrage.
|
||||
- Comme prévu par le drapeau `bConfigured` : pas d'INI et pas d'action du joueur, donc pas de ligne « qualite appliquee » (état moteur préservé).
|
||||
- Pas de `Video : controles ajoutes ...` : le menu Settings n'a pas été ouvert (attendu en `-nullrhi` sans interaction).
|
||||
- Pas de `Warning` ni d'`Error` du sous-système vidéo. Une seule `Error` dans le journal : `LogPlayerController: InputMode:UIOnly - Attempting to focus Non-Focusable widget` (l.1468), sans lien avec VQ2 (UI du jeu).
|
||||
|
||||
## Reste à faire — BLOCKED-UI
|
||||
|
||||
Test manuel Settings non automatisable ici. À faire à la main, puis relire le journal :
|
||||
|
||||
1. Ouvrir Settings > onglet vidéo : les 6 lignes `LPVideo*` doivent apparaître avant les lignes `LPColor*` ; `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` doivent être masqués.
|
||||
Journal attendu : `Video : controles ajoutes avant les reglages de couleur`.
|
||||
2. Déplacer `LPVideoOverall` : les 4 sliders suivent (`SetLevels(..., true)`) ; modifier un slider fait passer Overall à Custom.
|
||||
3. Bouton `LPVideoRecommended` : log `Recommended = stub (High)`.
|
||||
4. Quitter et relancer : `Saved/Config/Windows/LPVideoQuality.ini` relu (`Video : relu Overall ...`).
|
||||
5. Onglets Audio, Lewd et Clavier non perturbés ; `BackButton` toujours visible.
|
||||
|
||||
## Synthèse
|
||||
|
||||
| Critère | Statut |
|
||||
|---|---|
|
||||
| Compile OK (VQ2-IMPL) | PASS |
|
||||
| Code complet (`SetLevels`, `RefreshWidgets`, `EnsureControls`) | PASS |
|
||||
| `CategoryOf` (`LPVideo*`, `TabHidden`) | PASS |
|
||||
| Démarrage sans crash + log `Load` | PASS |
|
||||
| Test manuel Settings | BLOCKED-UI |
|
||||
70
Docs/Migration/etat/VQ/VQ3/VQ3-IMPL-H.md
Normal file
70
Docs/Migration/etat/VQ/VQ3/VQ3-IMPL-H.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# VQ3-IMPL-H — Bouton Recommended (VRAM)
|
||||
|
||||
**Fiche :** VQ3-T1
|
||||
**Rôle :** A-IMPL-H
|
||||
**Date :** 07 octobre 2026
|
||||
**Entrée :** `VQ2-IMPL-H.md` (stub), `VQ2-VAL.md`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Attendu (consigne) | `claude-sonnet-5-5-high` (repli M3) |
|
||||
| **Réel** | `claude-sonnet-5-5-high` (Claude Sonnet 5.5) |
|
||||
|
||||
## Fichiers
|
||||
|
||||
| Fichier | Action |
|
||||
|---|---|
|
||||
| `Source/LifePlay/LPVideoQualitySubsystem.cpp` | `OnRecommendedClicked` : calcul VRAM à la place du stub ; includes `DynamicRHI.h`, `RHI.h`, `RHIStats.h` ; helpers `LPVideoQuality::QueryDedicatedVideoMemoryBytes` et `LevelFromVramGB` ; constante `RecommendedStubLevel` remplacée par `RecommendedUnknownLevel` (2) |
|
||||
| `Source/LifePlay/LifePlay.Build.cs` | `PrivateDependencyModuleNames.Add("RHI")` (déjà fourni en transitif par `Engine`, rendu explicite) |
|
||||
|
||||
`LPVideoQualitySubsystem.h` inchangé (signature de `OnRecommendedClicked` identique).
|
||||
|
||||
## API choisie
|
||||
|
||||
Vérifiée dans les sources UE 5.5 :
|
||||
|
||||
- `RHIGetTextureMemoryStats(FTextureMemoryStats&)` : `DynamicRHI.h` l.1316 (appelle `GDynamicRHI`).
|
||||
- `FTextureMemoryStats::DedicatedVideoMemory` : `RHIStats.h` l.15, `int64` en octets, **-1 si inconnu**.
|
||||
- D3D12 : `FD3D12DynamicRHI::RHIGetTextureMemoryStats` (`D3D12Texture.cpp` l.394) renseigne `DedicatedVideoMemory` avec `GDedicatedVideoMemory` (hardware, fixé à la création du device).
|
||||
|
||||
Garde : `GDynamicRHI` nul → 0 ; valeur négative → `FMath::Max(.., 0)` → 0.
|
||||
|
||||
## Seuils
|
||||
|
||||
| VRAM dédiée | Niveau | Libellé |
|
||||
|---|---|---|
|
||||
| inconnue (0) | 2 | High (défaut) |
|
||||
| < 4 Go | 0 | Low |
|
||||
| < 6 Go | 1 | Med |
|
||||
| < 10 Go | 2 | High |
|
||||
| ≥ 10 Go | 3 | Epic |
|
||||
|
||||
Go = octets / 1024³.
|
||||
|
||||
## Application
|
||||
|
||||
`SetLevels(FLPVideoLevels::FromOverall(Level), true)` — `SetLevels` vérifié (l.~280) :
|
||||
|
||||
- `bFromOverall && Overall >= 0` → `FromOverall` : les 4 groupes alignés sur le niveau, Overall = niveau ;
|
||||
- sinon (curseur individuel) → `Normalize()` : Overall = niveau commun ou -1 (Custom) ;
|
||||
- puis `bConfigured = true`, `ApplyToScalability()`, `RefreshWidgets()`, `Save()`.
|
||||
|
||||
Journal (Display) :
|
||||
|
||||
- `Video : Recommended = High (niveau 2), VRAM dediee detectee 8.0 Go`
|
||||
- `Video : Recommended = High (niveau 2), VRAM dediee inconnue (0 Go), defaut High`
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
Build.bat LifePlayEditor Win64 Development -Project="D:\Game\LifePlay-master 5.5\LifePlay.uproject"
|
||||
```
|
||||
|
||||
Résultat : **compile OK** (UHT sans erreur, `Module.LifePlay.cpp` compilé, `UnrealEditor-LifePlay.dll` relié, exit code 0, aucun warning sur les fichiers VQ3).
|
||||
|
||||
## Limites
|
||||
|
||||
- GPU intégré : la VRAM dédiée est petite (mémoire partagée avec la RAM) → Low ou Med. Choix assumé (seuil simple demandé) ; `SharedSystemMemory` non utilisé.
|
||||
- Non vérifié en exécution avec un vrai RHI (le bouton exige le menu Settings) : voir `VQ3-VAL.md`.
|
||||
58
Docs/Migration/etat/VQ/VQ3/VQ3-VAL.md
Normal file
58
Docs/Migration/etat/VQ/VQ3/VQ3-VAL.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# VQ3-VAL — Validation Recommended (VRAM) et sync Video
|
||||
|
||||
**Fiche :** VQ3-T2
|
||||
**Rôle :** A-VAL
|
||||
**Date :** 07 octobre 2026
|
||||
**Entrée :** `VQ3-IMPL-H.md`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Attendu | `claude-sonnet-5-5-high` (repli M3) |
|
||||
| **Réel** | `claude-sonnet-5-5-high` (Claude Sonnet 5.5) |
|
||||
|
||||
## Verdict : **PASS** (avec **BLOCKED-UI** sur le test manuel Settings)
|
||||
|
||||
## Checklist code-review
|
||||
|
||||
| Case | Résultat | Preuve |
|
||||
|---|---|---|
|
||||
| Chemin Overall : `FromOverall` existe | **PASS** | `LPVideoQualitySubsystem.cpp` : `FLPVideoLevels::FromOverall` met Overall, Shadows, Textures, Effects, AA au niveau borné (`ClampLevel`, 0..3). `OnOverallChanged` → `SetLevels(FromOverall(round(valeur)), true)` ; `SetLevels` avec `bFromOverall` et `Overall >= 0` réaligne les 4 groupes. |
|
||||
| Individuel → Normalize / Custom | **PASS** | `OnShadows/Textures/Effects/AAChanged` : copie de `Levels`, un champ modifié, `SetLevels(L, false)` → `Normalize()` : `Overall = (4 niveaux égaux) ? niveau : -1`. `LevelLabel(-1)` = « Custom » (`RefreshWidgets` affiche `Levels.Overall` pour la ligne Overall). |
|
||||
| Recommended : lecture VRAM et seuils | **PASS** | `QueryDedicatedVideoMemoryBytes` (garde `GDynamicRHI`, `max(.., 0)`), `LevelFromVramGB` : 0 → 2 ; < 4 → 0 ; < 6 → 1 ; < 10 → 2 ; sinon 3. `OnRecommendedClicked` → `SetLevels(FromOverall(Level), true)`. API vérifiée dans `RHIStats.h` / `DynamicRHI.h` / `D3D12Texture.cpp` (UE 5.5). Compile OK. |
|
||||
| Persistance Load/Save | **PASS** (code) | `Save()` : section `LPVideoQuality`, `Version=1`, Overall, Shadows, Textures, Effects, AA vers `Saved/Config/Windows/LPVideoQuality.ini`, appelé par `SetLevels`. `Load()` (appelé par `Initialize`) relit les 4 niveaux, `Normalize()` recalcule Overall, `bConfigured = true` si au moins une clé ; `ApplyToScalability()` suit dans `Initialize`. |
|
||||
| Compile `LifePlayEditor` | **PASS** | exit 0, DLL `UnrealEditor-LifePlay.dll` relié (voir IMPL-H). |
|
||||
| Non-régression démarrage `-nullrhi` | **PASS** | voir ci-dessous. |
|
||||
| Test manuel Settings (clic Recommended) | **BLOCKED-UI** | non automatisable ici. |
|
||||
|
||||
## Smoke `-nullrhi`
|
||||
|
||||
Commande : `UnrealEditor-Cmd.exe LifePlay.uproject -game -nullrhi -nosound -unattended -nosplash -ExecCmds="quit"`, exit code 0, environ 47 s. Journal : `Saved/Logs/VQ3VAL.log`.
|
||||
|
||||
```
|
||||
LogLifePlay: Display: Video : pas de .../Saved/Config/Windows/LPVideoQuality.ini, niveaux lus depuis Scalability (Overall 3, Shadows 3, Textures 3, Effects 3, AA 3)
|
||||
```
|
||||
|
||||
- Aucun `Warning` ni `Error` du sous-système vidéo. Seule `Error` du journal : `LogPlayerController: InputMode:UIOnly ... Non-Focusable widget` (déjà vue en VQ2-VAL, sans lien).
|
||||
- Le clic n'est pas déclenché en `-nullrhi` : ce smoke ne couvre que l'absence de régression au démarrage.
|
||||
- Hors scope : en `-nullrhi`, le NullRHI renvoie des stats par défaut ; la garde « VRAM ≤ 0 → High » couvre ce cas.
|
||||
|
||||
## BLOCKED-UI — test manuel à faire
|
||||
|
||||
1. Settings > Video, cliquer `Recommended` : les 4 curseurs et Overall s'alignent sur le même niveau ; journal `Video : Recommended = <niveau> (niveau N), VRAM dediee detectee X Go` (X cohérent avec le GPU : < 4 Low, < 6 Med, < 10 High, sinon Epic).
|
||||
2. Vérifier `Saved/Config/Windows/LPVideoQuality.ini` : `Overall` = niveau choisi, 4 niveaux identiques.
|
||||
3. Modifier un curseur individuel : Overall passe à `Custom`. Cliquer `Recommended` : retour à un niveau unique.
|
||||
4. Quitter et relancer : `Video : relu Overall N, ...`.
|
||||
5. GPU intégré : noter le niveau obtenu (Low/Med attendu, limite documentée).
|
||||
|
||||
## Synthèse
|
||||
|
||||
| Critère | Statut |
|
||||
|---|---|
|
||||
| Overall → `FromOverall` | PASS |
|
||||
| Individuel → Custom | PASS |
|
||||
| Recommended VRAM | PASS |
|
||||
| Persistance Load/Save | PASS (code) |
|
||||
| Compile + démarrage `-nullrhi` | PASS |
|
||||
| Test manuel Settings | BLOCKED-UI |
|
||||
74
Docs/Migration/etat/VQ/VQ4/VQ4-VAL.md
Normal file
74
Docs/Migration/etat/VQ/VQ4/VQ4-VAL.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# VQ4-VAL — Validation finale V1 du chantier Video Quality
|
||||
|
||||
**Fiche :** VQ4-T1
|
||||
**Rôle :** A-VAL
|
||||
**Date :** 07 octobre 2026
|
||||
**Entrée :** `VQ1-SPEC.md` §8, `VQ2-SPEC.md` §13, `VQ2-VAL.md`, `VQ3-VAL.md`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Attendu | `claude-sonnet-5-5-high` |
|
||||
| **Réel** | `claude-sonnet-5-5-high` (Claude Sonnet 5.5) |
|
||||
|
||||
## Verdict : **PASS** (avec **BLOCKED-UI** sur le test manuel Settings, inchangé depuis VQ2 / VQ3)
|
||||
|
||||
## 1. Critères rejoués (code)
|
||||
|
||||
| # | Critère | Résultat | Preuve |
|
||||
|---|---|---|---|
|
||||
| 1 | `SetLevels` : sync Overall ↔ 4 curseurs | **PASS** | `LPVideoQualitySubsystem.cpp` l.289-303 : `bFromOverall && Overall >= 0` → `FromOverall` (4 groupes alignés), sinon `Normalize()` (Overall = niveau commun, sinon `-1` Custom). Enchaîne `ApplyToScalability`, `RefreshWidgets`, `Save`. Les 5 handlers `OnOverall/Shadows/Textures/Effects/AAChanged` (l.331-382) passent par `SetLevels`, avec garde `bRefreshing` contre la boucle. |
|
||||
| 2 | Recommended : VRAM | **PASS** | `QueryDedicatedVideoMemoryBytes` (garde `GDynamicRHI`, `RHIGetTextureMemoryStats`, `DedicatedVideoMemory`, `max(.., 0)`) l.36-44. `LevelFromVramGB` l.48-66 : ≤ 0 → 2 High ; < 4 → 0 ; < 6 → 1 ; < 10 → 2 ; sinon 3. `OnRecommendedClicked` l.384-398 : journal + `SetLevels(FromOverall(Level), true)`. |
|
||||
| 3 | `CategoryOf` : `LPVideo*` → Video | **PASS** | `LPSettingsSubsystem.cpp` l.153 : `StartsWith("LPColor") \|\| StartsWith("LPVideo")` → `TabVideo`. |
|
||||
| 4 | `CategoryOf` : `TabHidden` | **PASS** | l.41 `TabHidden = -2` ; l.146-151 : `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` → `TabHidden`. `ApplyRows` l.599 exclut `TabHidden`. |
|
||||
| 5 | `LPColor*` toujours `CategoryOf` Video | **PASS** | Même branche l.153 ; `LPColorGradeSubsystem.*` non modifié (`git status` : seuls `LPSettingsSubsystem.cpp`, `LifePlay.Build.cs`, `DefaultScalability.ini` et `LPVideoQualitySubsystem.*` bougent). |
|
||||
| 6 | `DefaultScalability.ini` : structure | **PASS** | 16 sections, uniquement `[ShadowQuality@0..3]`, `[TextureQuality@0..3]`, `[EffectsQuality@0..3]`, `[AntiAliasingQuality@0..3]`. |
|
||||
| 7 | `DefaultScalability.ini` : cvars L7c interdites | **PASS** | Voir §2 (0 occurrence). |
|
||||
| 8 | `DefaultEngine.ini` inchangé (L7c) | **PASS** | `git status` / `git diff --stat Config/DefaultEngine.ini` : vide. Valeurs L7c toujours présentes : `r.AntiAliasingMethod=2` (l.17), `r.DynamicGlobalIlluminationMethod=0` (l.22), `r.ReflectionMethod=2` (l.23), `r.Shadow.Virtual.Enable=0` (l.24), `DefaultGraphicsRHI_DX12` (l.129). |
|
||||
| 9 | Aucune cvar L7c dans le code VQ | **PASS** | `rg -i "Virtual\|Lumen\|AntiAliasingMethod\|ReflectionMethod"` sur `LPVideoQualitySubsystem.*` : seuls les mots-clés C++ `virtual` (`.h` l.55-56, faux positifs). |
|
||||
|
||||
## 2. Grep `DefaultScalability.ini` (re-exécuté)
|
||||
|
||||
Commande (insensible à la casse) :
|
||||
|
||||
```
|
||||
rg -n -i "Virtual|Lumen|GlobalIllumination|AntiAliasingMethod|ReflectionMethod|SSR|DynamicGlobal|ScreenSpaceReflection|r\.Shadow\.Virtual|DefaultFeature" Config/DefaultScalability.ini
|
||||
```
|
||||
|
||||
Résultat : **0 occurrence** (exit 1). Les cvars écrites par le fichier sont exactement : `fx.Niagara.QualityLevel`, `r.DetailMode`, `r.MaxAnisotropy`, `r.PostProcessAAQuality`, `r.RefractionQuality`, `r.Shadow.CSM.MaxCascades`, `r.Shadow.CSM.TransitionScale`, `r.Shadow.DistanceScale`, `r.Shadow.MaxCSMResolution`, `r.Shadow.MaxResolution`, `r.Shadow.PreShadowResolutionFactor`, `r.Shadow.RadiusThreshold`, `r.SSS.SampleSet`, `r.SSS.Scale`, `r.Streaming.MaxEffectiveScreenSize`, `r.Streaming.MipBias`, `r.Streaming.PoolSize`, `r.TranslucencyLightingVolumeDim`. Aucune ne touche GI, VSM, méthode d'AA ou reflets.
|
||||
|
||||
## 3. Smoke `-nullrhi` et DLL
|
||||
|
||||
| Contrôle | Résultat |
|
||||
|---|---|
|
||||
| `UnrealEditor-LifePlay.dll` | 23:29:07, postérieur à tous les sources (`LPVideoQualitySubsystem.cpp` 23:28:32, `.h` 23:21:19, `LPSettingsSubsystem.cpp` 23:20:54) : **à jour** |
|
||||
| Démarrage | `UnrealEditor-Cmd.exe LifePlay.uproject -game -nullrhi -nosound -unattended -nosplash -ExecCmds="quit"` : **exit 0**, 44 s. Journal : `Saved/Logs/VQ4VAL.log` |
|
||||
| Ligne vidéo | `LogLifePlay: Display: Video : pas de .../LPVideoQuality.ini, niveaux lus depuis Scalability (Overall 3, Shadows 3, Textures 3, Effects 3, AA 3)` |
|
||||
| Couleur | `Couleur : relu Brightness 0.00, ... depuis .../LPColorGrade.ini` (sous-système couleur intact) |
|
||||
| Warnings / Errors vidéo | aucun. Seule `Error` : `LogPlayerController: InputMode:UIOnly ... Non-Focusable widget` (déjà vue en VQ2/VQ3, sans lien) |
|
||||
|
||||
Le smoke ne couvre que le démarrage : le menu Settings n'est pas ouvert, donc pas de ligne `controles ajoutes`.
|
||||
|
||||
## 4. BLOCKED-UI (non automatisable ici)
|
||||
|
||||
À faire à la main, hors clôture documentaire :
|
||||
|
||||
1. Settings > Video : 6 lignes `LPVideo*` avant `LPColor*` ; `ShadowQuality`, `PostProcessQuality`, `PostProcessAAQuality` masqués, y compris après aller-retour d'onglet.
|
||||
2. Overall 0→3 : les 4 curseurs suivent ; un curseur individuel → `Custom`.
|
||||
3. `Recommended` : journal `VRAM dediee detectee X Go`, niveau conforme aux seuils (GPU intégré : Low/Med, limite documentée).
|
||||
4. Relance : `Video : relu Overall N, ...` depuis `LPVideoQuality.ini`.
|
||||
5. En jeu, niveau 3 : GI dynamique, VSM et AA (TAA) inchangés ; lecture console `sg.*` / `r.*`.
|
||||
|
||||
## 5. Synthèse
|
||||
|
||||
| Critère | Statut |
|
||||
|---|---|
|
||||
| `SetLevels` sync | PASS |
|
||||
| Recommended VRAM | PASS |
|
||||
| `CategoryOf` `LPVideo*` + `TabHidden` | PASS |
|
||||
| `DefaultScalability.ini` sans cvars L7c | PASS (grep : 0) |
|
||||
| `LPColor*` toujours Video | PASS |
|
||||
| `DefaultEngine.ini` inchangé | PASS |
|
||||
| DLL récente + smoke `-nullrhi` | PASS |
|
||||
| Test manuel Settings | BLOCKED-UI |
|
||||
73
Docs/Migration/etat/VQ/VQ5/VQ5-VAL.md
Normal file
73
Docs/Migration/etat/VQ/VQ5/VQ5-VAL.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# VQ5-VAL — Annexe : chemins de code Overall / Custom / Recommended
|
||||
|
||||
**Fiche :** VQ5-T2
|
||||
**Rôle :** A-VAL (annexe de `Docs/Migration/24_Eval_Agents_VideoQuality.md`)
|
||||
**Date :** 07 octobre 2026
|
||||
**Entrée :** `VQ2-VAL.md`, `VQ3-VAL.md`, `VQ4-VAL.md`, `Source/LifePlay/LPVideoQualitySubsystem.cpp`
|
||||
|
||||
## Modèle
|
||||
|
||||
| | Modèle |
|
||||
|---|---|
|
||||
| Attendu | `claude-opus-5-5-medium` (M4) |
|
||||
| **Réel** | `claude-opus-5-5-medium` (Claude Opus 5.5) |
|
||||
|
||||
## Verdict : **PASS (code)** — **BLOCKED-UI** inchangé pour l'exécution des clics
|
||||
|
||||
## 1. Choix : note annexe plutôt que nouveau smoke `-nullrhi`
|
||||
|
||||
Un nouveau démarrage `-nullrhi` n'apporterait rien de plus que `VQ4VAL.log` :
|
||||
|
||||
- aucun code n'a changé depuis VQ4 (`git diff --stat` : seuls `LPSettingsSubsystem.cpp`, `LifePlay.Build.cs` et le journal ; DLL 23:29:07 postérieure aux sources, cf. VQ4-VAL §3) ;
|
||||
- les trois chemins Overall / Custom / Recommended ne s'exécutent que sur un événement du menu Settings (curseur ou bouton). Le sous-système n'expose aucune commande console : `-ExecCmds` ne peut pas les atteindre. En `-nullrhi`, `RHIGetTextureMemoryStats` ne renvoie de toute façon pas de VRAM réelle.
|
||||
|
||||
Le smoke VQ4 est donc réutilisé comme preuve de démarrage, et cette annexe relie chaque chemin à son code.
|
||||
|
||||
## 2. Preuve de démarrage réutilisée (`Saved/Logs/VQ4VAL.log`, relu)
|
||||
|
||||
```
|
||||
l.1121 LogLifePlay: Display: Couleur : relu Brightness 0.00, Saturation 1.00, Contrast 1.00, White Balance 6500, Tint 0.00 depuis .../LPColorGrade.ini
|
||||
l.1153 LogLifePlay: Display: Video : pas de .../LPVideoQuality.ini, niveaux lus depuis Scalability (Overall 3, Shadows 3, Textures 3, Effects 3, AA 3)
|
||||
```
|
||||
|
||||
- Aucun `Warning` ni `Error` `LogLifePlay`. Seules lignes contenant `Error` : l.12 (Trace Server, accès refusé, sans lien) et l.1468 (`InputMode:UIOnly ... Non-Focusable widget`, déjà vue en VQ2/VQ3).
|
||||
- Chemin couvert : `Initialize` → `Load` → `ReadFromScalability` → `Normalize` (Overall = 3 car les 4 groupes sont égaux), puis `ApplyToScalability` sort sans rien écrire (`bConfigured == false`).
|
||||
|
||||
## 3. Chemins de code (`LPVideoQualitySubsystem.cpp`)
|
||||
|
||||
| Chemin | Appel | Code | Résultat attendu | Statut |
|
||||
|---|---|---|---|---|
|
||||
| **Overall** | curseur `LPVideoOverall` → `OnOverallChanged` (l.331) | `SetLevels(FromOverall(RoundToInt(V)), true)` ; `FromOverall` (l.116) borne 0..3 et met les 5 champs au même niveau ; `SetLevels` (l.289) prend la branche `bFromOverall && Overall >= 0` | 4 groupes = N, readout `Low/Med/High/Epic`, `Save`, `ApplyToScalability` | **PASS (code)** |
|
||||
| **Custom** | curseur individuel → `OnShadows/Textures/Effects/AAChanged` (l.340-382) | copie de `Levels`, un champ modifié, `SetLevels(L, false)` → `Normalize` (l.128) : `Overall = (4 égaux) ? niveau : -1` | `LevelLabel(-1)` = `Custom` (l.150) ; curseur Overall placé sur la moyenne arrondie (l.314) | **PASS (code)** |
|
||||
| **Retour depuis Custom** | ramener les 4 curseurs au même niveau | même `Normalize` | Overall réaffiche ce niveau | **PASS (code)** |
|
||||
| **Recommended** | bouton `LPVideoRecommended` → `OnRecommendedClicked` (l.384, lié l.465) | `QueryDedicatedVideoMemoryBytes` (l.36 : garde `GDynamicRHI`, `max(.., 0)`) → `LevelFromVramGB` (l.48 : ≤ 0 → 2 ; < 4 → 0 ; < 6 → 1 ; < 10 → 2 ; sinon 3) → `SetLevels(FromOverall(Level), true)` | même chemin que Overall ; journal `Recommended = ... VRAM dediee ...` | **PASS (code)** |
|
||||
| Anti-boucle | `RefreshWidgets` (l.306) | garde `bRefreshing` ; chaque handler sort si elle est levée | `SetValue` ne relance pas `SetLevels` | **PASS (code)** |
|
||||
| Ré-application | `Tick` (l.168) nouveau monde de jeu ; fin d'`EnsureControls` (l.621) | `ApplyToScalability` | le Blueprint ne réécrase pas les niveaux au chargement / à l'ouverture du menu | **PASS (code)** |
|
||||
| Exécution réelle des clics | menu Settings ouvert, vrai RHI | — | journaux `controles ajoutes`, `enregistres dans`, `Recommended = ...` | **BLOCKED-UI** |
|
||||
|
||||
## 4. Constat hors des trois chemins (relevé pendant VQ5)
|
||||
|
||||
Le point 1 de `VQ1-IMPL.md` §4 (« confronter chaque valeur à `Engine/Config/BaseScalability.ini` ») n'a été repris par aucune fiche VAL. Comparaison faite ici (`D:\Game\Launcher\Engine\UE_5.5\Engine\Config\BaseScalability.ini`), écarts réels au niveau **3 (Epic)**, celui que le moteur applique au démarrage (`Overall 3` ci-dessus) :
|
||||
|
||||
| cvar | Moteur 5.5 | Projet | Effet |
|
||||
|---|---|---|---|
|
||||
| `r.Shadow.CSM.MaxCascades` | 10 | 4 | moins de cascades au loin |
|
||||
| `r.Shadow.RadiusThreshold` | 0.01 | 0.03 | les petits objets perdent leur ombre plus tôt |
|
||||
| `r.DetailMode` | 3 | 2 | composants « detail mode » Epic masqués |
|
||||
| `r.RefractionQuality` | 2 | 3 | **au-dessus** du palier Epic moteur (contraire à VQ1-SPEC §3) |
|
||||
| `r.PostProcessAAQuality` | (non fixé par `AntiAliasingQuality@3`) | 4 | valeur imposée |
|
||||
|
||||
Aux niveaux 0–2, d'autres écarts existent (`r.Streaming.PoolSize` 200/400/700 contre 400/600/800, `r.Streaming.MipBias@0` 2.5 contre 16, `r.Shadow.MaxResolution@2` 2048 contre 1024, etc.).
|
||||
|
||||
Conséquence : `DefaultScalability.ini` modifie le rendu Epic **pour tous les joueurs, même sans ouvrir l'onglet Video**. L'affirmation « sans INI, aucun changement par rapport à l'état actuel » (VQ2-SPEC §6.2, doc 23 §2.1) n'est vraie que pour les niveaux `sg.*`, pas pour les cvars qu'ils appliquent. Aucun verrou L7c n'est touché (GI, VSM, méthode d'AA, reflets), donc les verdicts VQ1 et VQ4 tiennent sur leur contrat. En revanche, la baseline visuelle de L7c n'est plus garantie au niveau Epic. Suite proposée : voir la recommandation 1 du doc 24.
|
||||
|
||||
## 5. Synthèse
|
||||
|
||||
| Critère | Statut |
|
||||
|---|---|
|
||||
| Overall → `FromOverall` → 4 groupes | PASS (code) |
|
||||
| Individuel → `Normalize` → `Custom` / retour niveau | PASS (code) |
|
||||
| Recommended → VRAM → `FromOverall` | PASS (code) |
|
||||
| Démarrage `-nullrhi` (preuve VQ4 relue) | PASS |
|
||||
| Clics réels dans Settings | BLOCKED-UI |
|
||||
| Valeurs `DefaultScalability.ini` vs moteur | **Réserve** (écarts Epic, §4) |
|
||||
|
|
@ -21,7 +21,10 @@
|
|||
"Plugins": [
|
||||
{
|
||||
"Name": "LPAnimCore",
|
||||
"Enabled": true
|
||||
"Enabled": true,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "LPSandbox",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "Components/ButtonSlot.h"
|
||||
#include "Components/CanvasPanel.h"
|
||||
#include "Components/CanvasPanelSlot.h"
|
||||
#include "Components/CheckBox.h"
|
||||
#include "Components/HorizontalBox.h"
|
||||
#include "Components/HorizontalBoxSlot.h"
|
||||
#include "Components/Image.h"
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
#include "Components/OverlaySlot.h"
|
||||
#include "Components/ScrollBox.h"
|
||||
#include "Components/SizeBox.h"
|
||||
#include "Components/SpinBox.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Components/PanelWidget.h"
|
||||
#include "Components/VerticalBox.h"
|
||||
|
|
@ -24,6 +26,7 @@
|
|||
#include "LPKeyboardSubsystem.h"
|
||||
#include "LifePlay.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "Styling/SlateTypes.h"
|
||||
#include "UObject/TextProperty.h"
|
||||
|
||||
namespace LPSettingsUi
|
||||
|
|
@ -37,6 +40,8 @@ namespace LPSettingsUi
|
|||
constexpr int32 TabKeyboard = 3;
|
||||
constexpr int32 TabLewd = 4;
|
||||
constexpr int32 TabMisc = 5;
|
||||
constexpr int32 TabAlways = -1;
|
||||
constexpr int32 TabHidden = -2;
|
||||
|
||||
FString VolumeLabel(const FString& Text)
|
||||
{
|
||||
|
|
@ -96,6 +101,8 @@ namespace LPSettingsUi
|
|||
return Visibility != ESlateVisibility::Collapsed && Visibility != ESlateVisibility::Hidden;
|
||||
}
|
||||
|
||||
// Libelle visible seulement : les infobulles provoquent des faux positifs
|
||||
// (ex. "on screen" → Video, "prefer quality" → Video pour NewAnimationsOnly).
|
||||
FString RowText(const UWidget* Widget)
|
||||
{
|
||||
FString Text;
|
||||
|
|
@ -107,7 +114,6 @@ namespace LPSettingsUi
|
|||
{
|
||||
Text += Setting->GetPropertyValue_InContainer(Widget).ToString();
|
||||
}
|
||||
Text += TEXT(" ") + Widget->GetToolTipText().ToString();
|
||||
TArray<UWidget*> Pending;
|
||||
Pending.Add(const_cast<UWidget*>(Widget));
|
||||
while (Pending.Num() > 0)
|
||||
|
|
@ -139,9 +145,31 @@ namespace LPSettingsUi
|
|||
{
|
||||
if (Widget && Widget->GetFName() == TEXT("BackButton"))
|
||||
{
|
||||
return -1;
|
||||
return TabAlways;
|
||||
}
|
||||
if (Widget && Widget->GetName().StartsWith(TEXT("LPColor")))
|
||||
// Reglages qualite video remplaces par les lignes LPVideo* (LPVideoQualitySubsystem).
|
||||
if (Widget && (Widget->GetFName() == TEXT("ShadowQuality")
|
||||
|| Widget->GetFName() == TEXT("PostProcessQuality")
|
||||
|| Widget->GetFName() == TEXT("PostProcessAAQuality")))
|
||||
{
|
||||
return TabHidden;
|
||||
}
|
||||
// Noms stables : evite les collisions de mots-cles sur le libelle.
|
||||
if (Widget)
|
||||
{
|
||||
const FName Name = Widget->GetFName();
|
||||
if (Name == TEXT("NewAnimationsOnly"))
|
||||
{
|
||||
return TabGeneral;
|
||||
}
|
||||
if (Name == TEXT("ShowFPS") || Name == TEXT("ScreenPercentage") || Name == TEXT("FullscreenButton")
|
||||
|| Name == TEXT("ToonShading") || Name == TEXT("Censore") || Name == TEXT("CPUSkinning")
|
||||
|| Name == TEXT("DisableFurEffect"))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
}
|
||||
if (Widget && (Widget->GetName().StartsWith(TEXT("LPColor")) || Widget->GetName().StartsWith(TEXT("LPVideo"))))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
|
|
@ -150,10 +178,14 @@ namespace LPSettingsUi
|
|||
{
|
||||
return TabAudio;
|
||||
}
|
||||
if (Text.Contains(TEXT("fps")) || Text.Contains(TEXT("screen")) || Text.Contains(TEXT("shadow")) || Text.Contains(TEXT("quality"))
|
||||
|| Text.Contains(TEXT("fullscreen")) || Text.Contains(TEXT("window")) || Text.Contains(TEXT("toon")) || Text.Contains(TEXT("depth of field"))
|
||||
|| Text.Contains(TEXT("censor")) || Text.Contains(TEXT("morph")) || Text.Contains(TEXT("dog")) || Text.Contains(TEXT("jiggle"))
|
||||
|| Text.Contains(TEXT("resolution")) || Text.Contains(TEXT("vsync")) || Text.Contains(TEXT("brightness")))
|
||||
if (Text.Contains(TEXT("fps")) || Text.Contains(TEXT("screen percentage")) || Text.Contains(TEXT("shadow"))
|
||||
|| Text.Contains(TEXT("fullscreen")) || Text.Contains(TEXT("window")) || Text.Contains(TEXT("toon"))
|
||||
|| Text.Contains(TEXT("depth of field")) || Text.Contains(TEXT("censor")) || Text.Contains(TEXT("morph"))
|
||||
|| Text.Contains(TEXT("dog")) || Text.Contains(TEXT("jiggle")) || Text.Contains(TEXT("resolution"))
|
||||
|| Text.Contains(TEXT("vsync")) || Text.Contains(TEXT("brightness"))
|
||||
|| (Text.Contains(TEXT("quality")) && (Text.Contains(TEXT("shadow")) || Text.Contains(TEXT("aa "))
|
||||
|| Text.Contains(TEXT("anti-alias")) || Text.Contains(TEXT("post process")) || Text.Contains(TEXT("pp "))
|
||||
|| Text.Contains(TEXT("overall")) || Text.Contains(TEXT("texture")) || Text.Contains(TEXT("effect")))))
|
||||
{
|
||||
return TabVideo;
|
||||
}
|
||||
|
|
@ -173,6 +205,182 @@ namespace LPSettingsUi
|
|||
return TabMisc;
|
||||
}
|
||||
|
||||
void WireMainMenuGI(UUserWidget* Menu, UGameInstance* GI)
|
||||
{
|
||||
if (!Menu || !GI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FObjectPropertyBase* Prop = CastField<FObjectPropertyBase>(Menu->GetClass()->FindPropertyByName(TEXT("GI")));
|
||||
if (!Prop || Prop->GetObjectPropertyValue_InContainer(Menu))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!GI->IsA(Prop->PropertyClass))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Prop->SetObjectPropertyValue_InContainer(Menu, GI);
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Reglages : MainMenu.GI rattache (evite les spinbox / cases None sur l'ecran titre)"));
|
||||
}
|
||||
|
||||
void CollectCheckBoxes(UWidget* Widget, TArray<UCheckBox*>& Out)
|
||||
{
|
||||
if (!Widget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (UCheckBox* Box = Cast<UCheckBox>(Widget))
|
||||
{
|
||||
Out.Add(Box);
|
||||
return;
|
||||
}
|
||||
if (UUserWidget* User = Cast<UUserWidget>(Widget))
|
||||
{
|
||||
CollectCheckBoxes(User->GetRootWidget(), Out);
|
||||
}
|
||||
if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
|
||||
{
|
||||
for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index)
|
||||
{
|
||||
CollectCheckBoxes(Panel->GetChildAt(Index), Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CollectSpinBoxes(UWidget* Widget, TArray<USpinBox*>& Out)
|
||||
{
|
||||
if (!Widget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (USpinBox* Box = Cast<USpinBox>(Widget))
|
||||
{
|
||||
Out.Add(Box);
|
||||
return;
|
||||
}
|
||||
if (UUserWidget* User = Cast<UUserWidget>(Widget))
|
||||
{
|
||||
CollectSpinBoxes(User->GetRootWidget(), Out);
|
||||
}
|
||||
if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
|
||||
{
|
||||
for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index)
|
||||
{
|
||||
CollectSpinBoxes(Panel->GetChildAt(Index), Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TintBrush(FSlateBrush& Brush, const FLinearColor& Tint)
|
||||
{
|
||||
Brush.TintColor = FSlateColor(Tint);
|
||||
}
|
||||
|
||||
// Cases et spinboxes BP : style Slate clair → illisible sur fond flou clair.
|
||||
// Le curseur Contrast du color grading les revelait sans corriger le style.
|
||||
void HardenSettingsControls(UScrollBox* Scroll)
|
||||
{
|
||||
if (!Scroll)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FCheckBoxStyle CheckStyle = FCoreStyle::Get().GetWidgetStyle<FCheckBoxStyle>("Checkbox");
|
||||
const FLinearColor Unchecked(0.08f, 0.08f, 0.08f, 1.f);
|
||||
const FLinearColor UncheckedHover(0.18f, 0.18f, 0.18f, 1.f);
|
||||
const FLinearColor Checked(0.12f, 0.42f, 0.95f, 1.f);
|
||||
const FLinearColor CheckedHover(0.2f, 0.5f, 1.f, 1.f);
|
||||
TintBrush(CheckStyle.UncheckedImage, Unchecked);
|
||||
TintBrush(CheckStyle.UncheckedHoveredImage, UncheckedHover);
|
||||
TintBrush(CheckStyle.UncheckedPressedImage, Unchecked);
|
||||
TintBrush(CheckStyle.CheckedImage, Checked);
|
||||
TintBrush(CheckStyle.CheckedHoveredImage, CheckedHover);
|
||||
TintBrush(CheckStyle.CheckedPressedImage, Checked);
|
||||
TintBrush(CheckStyle.UndeterminedImage, Unchecked);
|
||||
TintBrush(CheckStyle.UndeterminedHoveredImage, UncheckedHover);
|
||||
TintBrush(CheckStyle.UndeterminedPressedImage, Unchecked);
|
||||
CheckStyle.BorderBackgroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f));
|
||||
CheckStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f));
|
||||
|
||||
FSpinBoxStyle SpinStyle = FCoreStyle::Get().GetWidgetStyle<FSpinBoxStyle>("SpinBox");
|
||||
const FLinearColor SpinBg(0.06f, 0.06f, 0.06f, 0.95f);
|
||||
const FLinearColor SpinBgHover(0.12f, 0.12f, 0.12f, 0.98f);
|
||||
const FLinearColor SpinBgActive(0.16f, 0.16f, 0.16f, 1.f);
|
||||
TintBrush(SpinStyle.BackgroundBrush, SpinBg);
|
||||
TintBrush(SpinStyle.HoveredBackgroundBrush, SpinBgHover);
|
||||
TintBrush(SpinStyle.ActiveBackgroundBrush, SpinBgActive);
|
||||
TintBrush(SpinStyle.ActiveFillBrush, FLinearColor(0.2f, 0.45f, 0.9f, 0.85f));
|
||||
SpinStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f));
|
||||
|
||||
TArray<UCheckBox*> Boxes;
|
||||
TArray<USpinBox*> Spins;
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
CollectCheckBoxes(Scroll->GetChildAt(Index), Boxes);
|
||||
CollectSpinBoxes(Scroll->GetChildAt(Index), Spins);
|
||||
}
|
||||
|
||||
for (UCheckBox* Box : Boxes)
|
||||
{
|
||||
if (!Box)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Box->SetIsEnabled(true);
|
||||
Box->SetRenderOpacity(1.f);
|
||||
Box->SetWidgetStyle(CheckStyle);
|
||||
{
|
||||
FWidgetTransform Xform = Box->GetRenderTransform();
|
||||
if (FMath::Max(FMath::Abs(Xform.Scale.X), FMath::Abs(Xform.Scale.Y)) < 1.5f)
|
||||
{
|
||||
Xform.Scale = FVector2D(2.f, 2.f);
|
||||
Box->SetRenderTransform(Xform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (USpinBox* Spin : Spins)
|
||||
{
|
||||
if (!Spin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Spin->SetIsEnabled(true);
|
||||
Spin->SetRenderOpacity(1.f);
|
||||
Spin->SetWidgetStyle(SpinStyle);
|
||||
Spin->SetForegroundColor(FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f)));
|
||||
FSlateFontInfo Font = Spin->GetFont();
|
||||
Font.OutlineSettings.OutlineSize = 2;
|
||||
Font.OutlineSettings.bApplyOutlineToDropShadows = true;
|
||||
Font.OutlineSettings.OutlineColor = FLinearColor(0.f, 0.f, 0.f, 0.95f);
|
||||
if (Font.Size < 18.f)
|
||||
{
|
||||
Font.Size = 18.f;
|
||||
}
|
||||
Spin->SetFont(Font);
|
||||
}
|
||||
|
||||
if (UWidget* ShowFPS = [&]() -> UWidget*
|
||||
{
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
if (UWidget* Child = Scroll->GetChildAt(Index))
|
||||
{
|
||||
if (Child->GetFName() == TEXT("ShowFPS"))
|
||||
{
|
||||
return Child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}())
|
||||
{
|
||||
ShowFPS->SetIsEnabled(true);
|
||||
ShowFPS->SetRenderOpacity(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
UTextBlock* FirstText(UWidget* Widget)
|
||||
{
|
||||
if (!Widget)
|
||||
|
|
@ -220,7 +428,8 @@ namespace LPSettingsUi
|
|||
void ULPSettingsSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPSettingsSubsystem::Tick), 0.5f);
|
||||
// Chaque frame : le BP Settings peut reforcer Visible sur toutes les lignes entre deux ticks.
|
||||
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPSettingsSubsystem::Tick), 0.f);
|
||||
}
|
||||
|
||||
void ULPSettingsSubsystem::Deinitialize()
|
||||
|
|
@ -266,14 +475,22 @@ bool ULPSettingsSubsystem::Tick(float DeltaTime)
|
|||
}
|
||||
if (!bOpen)
|
||||
{
|
||||
bControlsHardened = false;
|
||||
return true;
|
||||
}
|
||||
EnsureTabs(Scroll);
|
||||
if (Scroll->GetChildrenCount() != LastChildCount)
|
||||
if (UUserWidget* Menu = Scroll->GetTypedOuter<UUserWidget>())
|
||||
{
|
||||
LastChildCount = Scroll->GetChildrenCount();
|
||||
ApplyRows();
|
||||
LPSettingsUi::WireMainMenuGI(Menu, GetGameInstance());
|
||||
}
|
||||
EnsureTabs(Scroll);
|
||||
const int32 ChildCount = Scroll->GetChildrenCount();
|
||||
if (!bControlsHardened || ChildCount != LastChildCount)
|
||||
{
|
||||
LPSettingsUi::HardenSettingsControls(Scroll);
|
||||
bControlsHardened = true;
|
||||
LastChildCount = ChildCount;
|
||||
}
|
||||
ApplyRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -586,7 +803,8 @@ void ULPSettingsSubsystem::ApplyRows()
|
|||
}
|
||||
LPSettingsUi::RenameVolumes(Child);
|
||||
const int32 Category = LPSettingsUi::CategoryOf(Child);
|
||||
const bool bShow = Category < 0 || (ActiveTab != LPSettingsUi::TabKeyboard && Category == ActiveTab);
|
||||
// -1 : toujours visible (BackButton) ; -2 : toujours replie.
|
||||
const bool bShow = Category == LPSettingsUi::TabAlways || (Category != LPSettingsUi::TabHidden && ActiveTab != LPSettingsUi::TabKeyboard && Category == ActiveTab);
|
||||
Child->SetVisibility(bShow ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
|
||||
}
|
||||
const bool bKeyboard = ActiveTab == LPSettingsUi::TabKeyboard;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ private:
|
|||
int32 ActiveTab = 0;
|
||||
int32 LastChildCount = -1;
|
||||
bool bKeyboardPage = false;
|
||||
bool bControlsHardened = false;
|
||||
|
||||
TWeakObjectPtr<UButton> SourceSettings;
|
||||
TWeakObjectPtr<UVerticalBox> OrderedMenu;
|
||||
|
|
|
|||
605
Source/LifePlay/LPVideoQualitySubsystem.cpp
Normal file
605
Source/LifePlay/LPVideoQualitySubsystem.cpp
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
// Voir LPVideoQualitySubsystem.h.
|
||||
|
||||
#include "LPVideoQualitySubsystem.h"
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Components/Button.h"
|
||||
#include "Components/ButtonSlot.h"
|
||||
#include "Components/HorizontalBox.h"
|
||||
#include "Components/HorizontalBoxSlot.h"
|
||||
#include "Components/PanelWidget.h"
|
||||
#include "Components/ScrollBox.h"
|
||||
#include "Components/ScrollBoxSlot.h"
|
||||
#include "Components/SizeBox.h"
|
||||
#include "Components/Slider.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/World.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "LifePlay.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "DynamicRHI.h"
|
||||
#include "RHI.h"
|
||||
#include "RHIStats.h"
|
||||
#include "Scalability.h"
|
||||
#include "Styling/CoreStyle.h"
|
||||
#include "Styling/SlateTypes.h"
|
||||
|
||||
namespace LPVideoQuality
|
||||
{
|
||||
constexpr int32 MaxLevel = 3;
|
||||
// Niveau retenu quand la VRAM est inconnue (0 ou -1) : High.
|
||||
constexpr int32 RecommendedUnknownLevel = 2;
|
||||
|
||||
// VRAM dediee en octets, 0 si inconnue (RHI absent, -nullrhi, valeur non renseignee).
|
||||
int64 QueryDedicatedVideoMemoryBytes()
|
||||
{
|
||||
if (!GDynamicRHI)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
FTextureMemoryStats Stats;
|
||||
RHIGetTextureMemoryStats(Stats);
|
||||
return FMath::Max<int64>(Stats.DedicatedVideoMemory, 0);
|
||||
}
|
||||
|
||||
// <4 Go Low, <6 Med, <10 High, sinon Epic ; 0 Go (inconnu) = High.
|
||||
int32 LevelFromVramGB(double VramGB)
|
||||
{
|
||||
if (VramGB <= 0.0)
|
||||
{
|
||||
return RecommendedUnknownLevel;
|
||||
}
|
||||
if (VramGB < 4.0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (VramGB < 6.0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (VramGB < 10.0)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
FString IniPath()
|
||||
{
|
||||
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config/Windows/LPVideoQuality.ini"));
|
||||
}
|
||||
|
||||
FSlateFontInfo MenuFont()
|
||||
{
|
||||
FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Regular", 20);
|
||||
Font.OutlineSettings.OutlineSize = 2;
|
||||
Font.OutlineSettings.bApplyOutlineToDropShadows = true;
|
||||
Font.OutlineSettings.OutlineColor = FLinearColor(0.f, 0.f, 0.f, 0.8f);
|
||||
return Font;
|
||||
}
|
||||
|
||||
void FindLook(UWidget* Widget, UTextBlock*& OutText, USlider*& OutSlider)
|
||||
{
|
||||
if (!Widget || (OutText && OutSlider))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!OutText)
|
||||
{
|
||||
OutText = Cast<UTextBlock>(Widget);
|
||||
}
|
||||
if (!OutSlider)
|
||||
{
|
||||
OutSlider = Cast<USlider>(Widget);
|
||||
}
|
||||
if (UUserWidget* User = Cast<UUserWidget>(Widget))
|
||||
{
|
||||
FindLook(User->GetRootWidget(), OutText, OutSlider);
|
||||
}
|
||||
if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
|
||||
{
|
||||
for (int32 Index = 0; Index < Panel->GetChildrenCount() && !(OutText && OutSlider); ++Index)
|
||||
{
|
||||
FindLook(Panel->GetChildAt(Index), OutText, OutSlider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 ClampLevel(int32 Level)
|
||||
{
|
||||
return FMath::Clamp(Level, 0, MaxLevel);
|
||||
}
|
||||
}
|
||||
|
||||
FLPVideoLevels FLPVideoLevels::FromOverall(int32 Level)
|
||||
{
|
||||
const int32 Clamped = LPVideoQuality::ClampLevel(Level);
|
||||
FLPVideoLevels Result;
|
||||
Result.Overall = Clamped;
|
||||
Result.Shadows = Clamped;
|
||||
Result.Textures = Clamped;
|
||||
Result.Effects = Clamped;
|
||||
Result.AA = Clamped;
|
||||
return Result;
|
||||
}
|
||||
|
||||
void FLPVideoLevels::Normalize()
|
||||
{
|
||||
Shadows = LPVideoQuality::ClampLevel(Shadows);
|
||||
Textures = LPVideoQuality::ClampLevel(Textures);
|
||||
Effects = LPVideoQuality::ClampLevel(Effects);
|
||||
AA = LPVideoQuality::ClampLevel(AA);
|
||||
Overall = (Shadows == Textures && Textures == Effects && Effects == AA) ? Shadows : -1;
|
||||
}
|
||||
|
||||
bool FLPVideoLevels::operator==(const FLPVideoLevels& Other) const
|
||||
{
|
||||
return Overall == Other.Overall && Shadows == Other.Shadows && Textures == Other.Textures && Effects == Other.Effects && AA == Other.AA;
|
||||
}
|
||||
|
||||
FString ULPVideoQualitySubsystem::LevelLabel(int32 Level)
|
||||
{
|
||||
switch (Level)
|
||||
{
|
||||
case 0: return TEXT("Low");
|
||||
case 1: return TEXT("Med");
|
||||
case 2: return TEXT("High");
|
||||
case 3: return TEXT("Epic");
|
||||
default: return TEXT("Custom");
|
||||
}
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
Load();
|
||||
ApplyToScalability();
|
||||
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPVideoQualitySubsystem::Tick), 0.5f);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::Deinitialize()
|
||||
{
|
||||
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
bool ULPVideoQualitySubsystem::Tick(float DeltaTime)
|
||||
{
|
||||
(void)DeltaTime;
|
||||
UWorld* World = GetWorld();
|
||||
if (World && World->IsGameWorld() && LastAppliedWorld.Get() != World)
|
||||
{
|
||||
// Le Blueprint peut reappliquer ses valeurs au chargement d'un niveau.
|
||||
LastAppliedWorld = World;
|
||||
ApplyToScalability();
|
||||
}
|
||||
EnsureControls();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::ReadFromScalability()
|
||||
{
|
||||
const Scalability::FQualityLevels Current = Scalability::GetQualityLevels();
|
||||
Levels.Shadows = Current.ShadowQuality;
|
||||
Levels.Textures = Current.TextureQuality;
|
||||
Levels.Effects = Current.EffectsQuality;
|
||||
Levels.AA = Current.AntiAliasingQuality;
|
||||
Levels.Normalize();
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::Load()
|
||||
{
|
||||
// Valeurs de repli : etat Scalability courant (premier lancement).
|
||||
ReadFromScalability();
|
||||
const FString Ini = LPVideoQuality::IniPath();
|
||||
FConfigFile File;
|
||||
File.Read(Ini);
|
||||
bool bAnyKey = false;
|
||||
auto Read = [&File, &bAnyKey](const TCHAR* Key, int32& Out)
|
||||
{
|
||||
FString Text;
|
||||
if (File.GetString(TEXT("LPVideoQuality"), Key, Text))
|
||||
{
|
||||
Out = FCString::Atoi(*Text);
|
||||
bAnyKey = true;
|
||||
}
|
||||
};
|
||||
// Overall est relu pour la trace mais ignore : Normalize() le recalcule.
|
||||
int32 StoredOverall = Levels.Overall;
|
||||
Read(TEXT("Overall"), StoredOverall);
|
||||
Read(TEXT("Shadows"), Levels.Shadows);
|
||||
Read(TEXT("Textures"), Levels.Textures);
|
||||
Read(TEXT("Effects"), Levels.Effects);
|
||||
Read(TEXT("AA"), Levels.AA);
|
||||
Levels.Normalize();
|
||||
if (bAnyKey)
|
||||
{
|
||||
bConfigured = true;
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : relu Overall %d, Shadows %d, Textures %d, Effects %d, AA %d depuis %s"), Levels.Overall, Levels.Shadows, Levels.Textures, Levels.Effects, Levels.AA, *Ini);
|
||||
}
|
||||
else
|
||||
{
|
||||
bConfigured = false;
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : pas de %s, niveaux lus depuis Scalability (Overall %d, Shadows %d, Textures %d, Effects %d, AA %d)"), *Ini, Levels.Overall, Levels.Shadows, Levels.Textures, Levels.Effects, Levels.AA);
|
||||
}
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::Save() const
|
||||
{
|
||||
const FString Ini = LPVideoQuality::IniPath();
|
||||
FConfigFile File;
|
||||
File.Read(Ini);
|
||||
auto Write = [&File](const TCHAR* Key, int32 Value)
|
||||
{
|
||||
File.SetString(TEXT("LPVideoQuality"), Key, *FString::FromInt(Value));
|
||||
};
|
||||
Write(TEXT("Version"), 1);
|
||||
Write(TEXT("Overall"), Levels.Overall);
|
||||
Write(TEXT("Shadows"), Levels.Shadows);
|
||||
Write(TEXT("Textures"), Levels.Textures);
|
||||
Write(TEXT("Effects"), Levels.Effects);
|
||||
Write(TEXT("AA"), Levels.AA);
|
||||
File.Write(Ini);
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : Overall %d, Shadows %d, Textures %d, Effects %d, AA %d enregistres dans %s"), Levels.Overall, Levels.Shadows, Levels.Textures, Levels.Effects, Levels.AA, *Ini);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::ApplyToScalability()
|
||||
{
|
||||
if (!bConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Ecrire uniquement les 4 sg.* en SetByConsole : SetQualityLevels appliquerait aussi
|
||||
// r.ScreenPercentage / r.Streaming.PoolSize / r.SSS.Scale (souvent deja poses par le BP
|
||||
// en SetByConsole) et spamait des warnings "SetByScalability ignored".
|
||||
auto SetSg = [](const TCHAR* Name, int32 Value)
|
||||
{
|
||||
if (IConsoleVariable* Variable = IConsoleManager::Get().FindConsoleVariable(Name))
|
||||
{
|
||||
Variable->Set(Value, ECVF_SetByConsole);
|
||||
}
|
||||
};
|
||||
SetSg(TEXT("sg.ShadowQuality"), Levels.Shadows);
|
||||
SetSg(TEXT("sg.TextureQuality"), Levels.Textures);
|
||||
SetSg(TEXT("sg.EffectsQuality"), Levels.Effects);
|
||||
SetSg(TEXT("sg.AntiAliasingQuality"), Levels.AA);
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : qualite appliquee (sg.ShadowQuality %d, sg.TextureQuality %d, sg.EffectsQuality %d, sg.AntiAliasingQuality %d)"), Levels.Shadows, Levels.Textures, Levels.Effects, Levels.AA);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::SetLevels(FLPVideoLevels NewLevels, bool bFromOverall)
|
||||
{
|
||||
if (bFromOverall && NewLevels.Overall >= 0)
|
||||
{
|
||||
NewLevels = FLPVideoLevels::FromOverall(NewLevels.Overall);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewLevels.Normalize();
|
||||
}
|
||||
Levels = NewLevels;
|
||||
bConfigured = true;
|
||||
ApplyToScalability();
|
||||
RefreshWidgets();
|
||||
Save();
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::RefreshWidgets()
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bRefreshing = true;
|
||||
const int32 Values[NumRows] = {
|
||||
Levels.Overall >= 0 ? Levels.Overall : LPVideoQuality::ClampLevel(FMath::RoundToInt((Levels.Shadows + Levels.Textures + Levels.Effects + Levels.AA) * 0.25f)),
|
||||
Levels.Shadows, Levels.Textures, Levels.Effects, Levels.AA
|
||||
};
|
||||
for (int32 Index = 0; Index < NumRows; ++Index)
|
||||
{
|
||||
if (USlider* Slider = Rows[Index].Slider.Get())
|
||||
{
|
||||
Slider->SetValue(static_cast<float>(Values[Index]));
|
||||
}
|
||||
if (UTextBlock* Readout = Rows[Index].Readout.Get())
|
||||
{
|
||||
Readout->SetText(FText::FromString(LevelLabel(Index == RowOverall ? Levels.Overall : Values[Index])));
|
||||
}
|
||||
}
|
||||
bRefreshing = false;
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnOverallChanged(float Value)
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetLevels(FLPVideoLevels::FromOverall(FMath::RoundToInt(Value)), true);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnShadowsChanged(float Value)
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FLPVideoLevels L = Levels;
|
||||
L.Shadows = LPVideoQuality::ClampLevel(FMath::RoundToInt(Value));
|
||||
SetLevels(L, false);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnTexturesChanged(float Value)
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FLPVideoLevels L = Levels;
|
||||
L.Textures = LPVideoQuality::ClampLevel(FMath::RoundToInt(Value));
|
||||
SetLevels(L, false);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnEffectsChanged(float Value)
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FLPVideoLevels L = Levels;
|
||||
L.Effects = LPVideoQuality::ClampLevel(FMath::RoundToInt(Value));
|
||||
SetLevels(L, false);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnAAChanged(float Value)
|
||||
{
|
||||
if (bRefreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FLPVideoLevels L = Levels;
|
||||
L.AA = LPVideoQuality::ClampLevel(FMath::RoundToInt(Value));
|
||||
SetLevels(L, false);
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::OnRecommendedClicked()
|
||||
{
|
||||
const int64 VramBytes = LPVideoQuality::QueryDedicatedVideoMemoryBytes();
|
||||
const double VramGB = static_cast<double>(VramBytes) / (1024.0 * 1024.0 * 1024.0);
|
||||
const int32 Level = LPVideoQuality::LevelFromVramGB(VramGB);
|
||||
if (VramBytes > 0)
|
||||
{
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : Recommended = %s (niveau %d), VRAM dediee detectee %.1f Go"), *LevelLabel(Level), Level, VramGB);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : Recommended = %s (niveau %d), VRAM dediee inconnue (0 Go), defaut High"), *LevelLabel(Level), Level);
|
||||
}
|
||||
SetLevels(FLPVideoLevels::FromOverall(Level), true);
|
||||
}
|
||||
|
||||
USlider* ULPVideoQualitySubsystem::AddRow(ERow Row, UScrollBox* Scroll, UWidget*& OutRow, const TCHAR* Name, const TCHAR* Label, FName Handler, const TCHAR* Tip, const FSlateFontInfo& Font, const FSlateColor& Color, const FSliderStyle* SliderStyle)
|
||||
{
|
||||
UHorizontalBox* Box = NewObject<UHorizontalBox>(Scroll, Name);
|
||||
UTextBlock* Caption = NewObject<UTextBlock>(Box);
|
||||
Caption->SetText(FText::FromString(Label));
|
||||
Caption->SetFont(Font);
|
||||
Caption->SetColorAndOpacity(Color);
|
||||
USizeBox* CaptionBox = NewObject<USizeBox>(Box);
|
||||
CaptionBox->SetWidthOverride(220.f);
|
||||
CaptionBox->AddChild(Caption);
|
||||
USlider* Slider = NewObject<USlider>(Box);
|
||||
if (SliderStyle)
|
||||
{
|
||||
Slider->SetWidgetStyle(*SliderStyle);
|
||||
}
|
||||
Slider->SetMinValue(0.f);
|
||||
Slider->SetMaxValue(static_cast<float>(LPVideoQuality::MaxLevel));
|
||||
Slider->SetStepSize(1.f);
|
||||
Slider->SetLocked(false);
|
||||
Slider->SetValue(0.f);
|
||||
UTextBlock* Readout = NewObject<UTextBlock>(Box);
|
||||
Readout->SetFont(Font);
|
||||
Readout->SetColorAndOpacity(Color);
|
||||
if (UHorizontalBoxSlot* Slot = Box->AddChildToHorizontalBox(CaptionBox))
|
||||
{
|
||||
Slot->SetVerticalAlignment(VAlign_Center);
|
||||
Slot->SetPadding(FMargin(10.f, 0.f, 20.f, 0.f));
|
||||
}
|
||||
if (UHorizontalBoxSlot* Slot = Box->AddChildToHorizontalBox(Slider))
|
||||
{
|
||||
Slot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||
Slot->SetVerticalAlignment(VAlign_Center);
|
||||
Slot->SetPadding(FMargin(0.f, 0.f, 20.f, 0.f));
|
||||
}
|
||||
if (UHorizontalBoxSlot* Slot = Box->AddChildToHorizontalBox(Readout))
|
||||
{
|
||||
Slot->SetVerticalAlignment(VAlign_Center);
|
||||
Slot->SetPadding(FMargin(0.f, 0.f, 10.f, 0.f));
|
||||
}
|
||||
Box->SetToolTipText(FText::FromString(Tip));
|
||||
FScriptDelegate Delegate;
|
||||
Delegate.BindUFunction(this, Handler);
|
||||
Slider->OnValueChanged.Add(Delegate);
|
||||
Rows[Row].Slider = Slider;
|
||||
Rows[Row].Readout = Readout;
|
||||
OutRow = Box;
|
||||
return Slider;
|
||||
}
|
||||
|
||||
UWidget* ULPVideoQualitySubsystem::MakeRecommendedRow(UScrollBox* Scroll, const FSlateFontInfo& Font, const FSlateColor& Color)
|
||||
{
|
||||
UHorizontalBox* Box = NewObject<UHorizontalBox>(Scroll, TEXT("LPVideoRecommended"));
|
||||
UButton* Button = NewObject<UButton>(Box);
|
||||
UTextBlock* Caption = NewObject<UTextBlock>(Button);
|
||||
Caption->SetText(FText::FromString(TEXT("Recommended")));
|
||||
Caption->SetFont(Font);
|
||||
Caption->SetColorAndOpacity(Color);
|
||||
Button->SetContent(Caption);
|
||||
if (UButtonSlot* ContentSlot = Cast<UButtonSlot>(Caption->Slot))
|
||||
{
|
||||
ContentSlot->SetHorizontalAlignment(HAlign_Left);
|
||||
ContentSlot->SetVerticalAlignment(VAlign_Center);
|
||||
ContentSlot->SetPadding(FMargin(10.f));
|
||||
}
|
||||
FScriptDelegate Delegate;
|
||||
Delegate.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnRecommendedClicked));
|
||||
Button->OnClicked.Add(Delegate);
|
||||
if (UHorizontalBoxSlot* Slot = Box->AddChildToHorizontalBox(Button))
|
||||
{
|
||||
Slot->SetVerticalAlignment(VAlign_Center);
|
||||
Slot->SetHorizontalAlignment(HAlign_Left);
|
||||
Slot->SetPadding(FMargin(10.f, 0.f, 0.f, 0.f));
|
||||
}
|
||||
Box->SetToolTipText(FText::FromString(TEXT("Apply the recommended preset for this PC.")));
|
||||
RecommendedButton = Button;
|
||||
return Box;
|
||||
}
|
||||
|
||||
void ULPVideoQualitySubsystem::EnsureControls()
|
||||
{
|
||||
UScrollBox* Scroll = nullptr;
|
||||
for (TObjectIterator<UScrollBox> It; It; ++It)
|
||||
{
|
||||
UScrollBox* Candidate = *It;
|
||||
if (!Candidate || Candidate->GetFName() != TEXT("SettingsScrollBox") || Candidate->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const UUserWidget* Owner = Candidate->GetTypedOuter<UUserWidget>();
|
||||
if (!Owner || Owner->HasAnyFlags(RF_ClassDefaultObject) || Owner->GetGameInstance() != GetGameInstance())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Scroll = Candidate;
|
||||
break;
|
||||
}
|
||||
if (!Scroll)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int32 InsertAt = INDEX_NONE;
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
if (UWidget* Child = Scroll->GetChildAt(Index))
|
||||
{
|
||||
if (Child->GetFName() == TEXT("LPVideoOverall"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (InsertAt == INDEX_NONE && Child->GetName().StartsWith(TEXT("LPColor")))
|
||||
{
|
||||
InsertAt = Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UTextBlock* SampleText = nullptr;
|
||||
USlider* SampleSlider = nullptr;
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
LPVideoQuality::FindLook(Scroll->GetChildAt(Index), SampleText, SampleSlider);
|
||||
}
|
||||
const FSlateFontInfo Font = SampleText ? SampleText->GetFont() : LPVideoQuality::MenuFont();
|
||||
const FSlateColor Color = SampleText ? SampleText->GetColorAndOpacity() : FSlateColor(FLinearColor(1.f, 1.f, 1.f, 0.95f));
|
||||
const FSliderStyle* SliderStyle = SampleSlider ? &SampleSlider->GetWidgetStyle() : nullptr;
|
||||
|
||||
// Nouvelle instance du menu : on repart de l'etat moteur tant que le joueur n'a rien choisi.
|
||||
if (!bConfigured)
|
||||
{
|
||||
ReadFromScalability();
|
||||
}
|
||||
|
||||
UWidget* Built[NumRows + 1] = { nullptr };
|
||||
AddRow(RowOverall, Scroll, Built[0], TEXT("LPVideoOverall"), TEXT("Overall Quality"), GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnOverallChanged), TEXT("Preset applied to shadows, textures, effects and anti-aliasing. Custom when they differ."), Font, Color, SliderStyle);
|
||||
AddRow(RowShadows, Scroll, Built[1], TEXT("LPVideoShadows"), TEXT("Shadows"), GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnShadowsChanged), TEXT("Shadow resolution and distance."), Font, Color, SliderStyle);
|
||||
AddRow(RowTextures, Scroll, Built[2], TEXT("LPVideoTextures"), TEXT("Textures"), GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnTexturesChanged), TEXT("Texture filtering and streaming budget."), Font, Color, SliderStyle);
|
||||
AddRow(RowEffects, Scroll, Built[3], TEXT("LPVideoEffects"), TEXT("Effects"), GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnEffectsChanged), TEXT("Translucency, refraction, subsurface scattering and particles."), Font, Color, SliderStyle);
|
||||
AddRow(RowAA, Scroll, Built[4], TEXT("LPVideoAA"), TEXT("Anti-Aliasing"), GET_FUNCTION_NAME_CHECKED(ULPVideoQualitySubsystem, OnAAChanged), TEXT("Anti-aliasing quality. The method itself is fixed."), Font, Color, SliderStyle);
|
||||
Built[5] = MakeRecommendedRow(Scroll, Font, Color);
|
||||
|
||||
bool bInserted = false;
|
||||
if (InsertAt != INDEX_NONE)
|
||||
{
|
||||
bInserted = true;
|
||||
for (int32 Index = 0; Index < NumRows + 1; ++Index)
|
||||
{
|
||||
UPanelSlot* PanelSlot = Scroll->InsertChildAt(InsertAt + Index, Built[Index]);
|
||||
if (!PanelSlot)
|
||||
{
|
||||
bInserted = false;
|
||||
break;
|
||||
}
|
||||
if (UScrollBoxSlot* Slot = Cast<UScrollBoxSlot>(PanelSlot))
|
||||
{
|
||||
Slot->SetPadding(FMargin(0.f, 0.f, 0.f, 10.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bInserted)
|
||||
{
|
||||
if (InsertAt != INDEX_NONE)
|
||||
{
|
||||
// Repli : InsertChildAt a echoue. On retire ce qui a pu etre insere, on ajoute en fin
|
||||
// puis on remet les LPColor* derriere en conservant leurs objets.
|
||||
UE_LOG(LogLifePlay, Warning, TEXT("Video : InsertChildAt a echoue, repli par AddChild et reordonnancement des reglages de couleur"));
|
||||
for (UWidget* Row : Built)
|
||||
{
|
||||
if (Row && Row->GetParent() == Scroll)
|
||||
{
|
||||
Scroll->RemoveChild(Row);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (UWidget* Row : Built)
|
||||
{
|
||||
if (UScrollBoxSlot* Slot = Cast<UScrollBoxSlot>(Scroll->AddChild(Row)))
|
||||
{
|
||||
Slot->SetPadding(FMargin(0.f, 0.f, 0.f, 10.f));
|
||||
}
|
||||
}
|
||||
if (InsertAt != INDEX_NONE)
|
||||
{
|
||||
TArray<UWidget*> Colors;
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
UWidget* Child = Scroll->GetChildAt(Index);
|
||||
if (Child && Child->GetName().StartsWith(TEXT("LPColor")))
|
||||
{
|
||||
Colors.Add(Child);
|
||||
}
|
||||
}
|
||||
for (UWidget* Child : Colors)
|
||||
{
|
||||
Scroll->RemoveChild(Child);
|
||||
}
|
||||
for (UWidget* Child : Colors)
|
||||
{
|
||||
if (UScrollBoxSlot* Slot = Cast<UScrollBoxSlot>(Scroll->AddChild(Child)))
|
||||
{
|
||||
Slot->SetPadding(FMargin(0.f, 0.f, 0.f, 10.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Les curseurs Blueprint remplaces par LPVideo* sont replies tout de suite ;
|
||||
// LPSettingsSubsystem les garde replies au changement d'onglet.
|
||||
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
|
||||
{
|
||||
if (UWidget* Child = Scroll->GetChildAt(Index))
|
||||
{
|
||||
const FName ChildName = Child->GetFName();
|
||||
if (ChildName == TEXT("ShadowQuality") || ChildName == TEXT("PostProcessQuality") || ChildName == TEXT("PostProcessAAQuality"))
|
||||
{
|
||||
Child->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefreshWidgets();
|
||||
// Nouvelle instance du menu : le Blueprint a pu reappliquer ses propres valeurs.
|
||||
ApplyToScalability();
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Video : controles ajoutes avant les reglages de couleur"));
|
||||
}
|
||||
112
Source/LifePlay/LPVideoQualitySubsystem.h
Normal file
112
Source/LifePlay/LPVideoQualitySubsystem.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Qualite video : un curseur Overall (Low a Epic) et quatre curseurs par groupe Scalability.
|
||||
//
|
||||
// Les quatre groupes sont les ombres, les textures, les effets et l'anti-aliasing
|
||||
// (sg.ShadowQuality, sg.TextureQuality, sg.EffectsQuality, sg.AntiAliasingQuality).
|
||||
// Les autres groupes Scalability, la GI, les VSM et la methode d'AA ne sont jamais ecrits.
|
||||
// Les lignes sont injectees dans SettingsScrollBox avant les reglages de couleur (LPColor*).
|
||||
// Enregistre dans Saved/Config/Windows/LPVideoQuality.ini, pas dans GameSettings.sav.
|
||||
// Voir Docs/Migration/etat/VQ/VQ2/VQ2-SPEC.md.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "Styling/SlateTypes.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "LPVideoQualitySubsystem.generated.h"
|
||||
|
||||
class UButton;
|
||||
class UScrollBox;
|
||||
class USlider;
|
||||
class UTextBlock;
|
||||
class UWidget;
|
||||
class UWorld;
|
||||
|
||||
USTRUCT()
|
||||
struct FLPVideoLevels
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// 0..3, ou -1 = Custom (les quatre groupes different).
|
||||
UPROPERTY()
|
||||
int32 Overall = 2;
|
||||
UPROPERTY()
|
||||
int32 Shadows = 2;
|
||||
UPROPERTY()
|
||||
int32 Textures = 2;
|
||||
UPROPERTY()
|
||||
int32 Effects = 2;
|
||||
UPROPERTY()
|
||||
int32 AA = 2;
|
||||
|
||||
// Les quatre groupes valent Level (clampe 0..3), Overall = Level.
|
||||
static FLPVideoLevels FromOverall(int32 Level);
|
||||
// Clamp 0..3 des quatre groupes ; Overall = valeur commune, sinon -1.
|
||||
void Normalize();
|
||||
bool operator==(const FLPVideoLevels& Other) const;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class LIFEPLAY_API ULPVideoQualitySubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
// Point d'entree unique de modification (UI, Recommended, VQ3).
|
||||
void SetLevels(FLPVideoLevels NewLevels, bool bFromOverall);
|
||||
FLPVideoLevels GetLevels() const { return Levels; }
|
||||
void RefreshWidgets();
|
||||
|
||||
UFUNCTION()
|
||||
void OnOverallChanged(float Value);
|
||||
UFUNCTION()
|
||||
void OnShadowsChanged(float Value);
|
||||
UFUNCTION()
|
||||
void OnTexturesChanged(float Value);
|
||||
UFUNCTION()
|
||||
void OnEffectsChanged(float Value);
|
||||
UFUNCTION()
|
||||
void OnAAChanged(float Value);
|
||||
UFUNCTION()
|
||||
void OnRecommendedClicked();
|
||||
|
||||
private:
|
||||
enum ERow
|
||||
{
|
||||
RowOverall,
|
||||
RowShadows,
|
||||
RowTextures,
|
||||
RowEffects,
|
||||
RowAA,
|
||||
NumRows
|
||||
};
|
||||
|
||||
struct FRowRefs
|
||||
{
|
||||
TWeakObjectPtr<USlider> Slider;
|
||||
TWeakObjectPtr<UTextBlock> Readout;
|
||||
};
|
||||
|
||||
bool Tick(float DeltaTime);
|
||||
void EnsureControls();
|
||||
void Load();
|
||||
void Save() const;
|
||||
void ApplyToScalability();
|
||||
void ReadFromScalability();
|
||||
USlider* AddRow(ERow Row, UScrollBox* Scroll, UWidget*& OutRow, const TCHAR* Name, const TCHAR* Label, FName Handler, const TCHAR* Tip, const FSlateFontInfo& Font, const FSlateColor& Color, const FSliderStyle* SliderStyle);
|
||||
UWidget* MakeRecommendedRow(UScrollBox* Scroll, const FSlateFontInfo& Font, const FSlateColor& Color);
|
||||
static FString LevelLabel(int32 Level);
|
||||
|
||||
FTSTicker::FDelegateHandle TickHandle;
|
||||
FRowRefs Rows[NumRows];
|
||||
TWeakObjectPtr<UButton> RecommendedButton;
|
||||
TWeakObjectPtr<UWorld> LastAppliedWorld;
|
||||
FLPVideoLevels Levels;
|
||||
// Vrai des qu'un INI existe ou que le joueur a modifie un niveau : seulement alors on ecrase l'etat moteur.
|
||||
bool bConfigured = false;
|
||||
// Garde anti-reentrance de RefreshWidgets.
|
||||
bool bRefreshing = false;
|
||||
};
|
||||
|
|
@ -19,6 +19,9 @@ public class LifePlay : ModuleRules
|
|||
// Portage 5.5 : necessaires a LPVictoryCompat (remplacement du Victory Plugin)
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { "ImageWrapper", "ApplicationCore" });
|
||||
|
||||
// VQ3 : ULPVideoQualitySubsystem::OnRecommendedClicked lit la VRAM dediee (RHIGetTextureMemoryStats).
|
||||
PrivateDependencyModuleNames.Add("RHI");
|
||||
|
||||
// Portage 5.5 (R-04) : ULPNavigationHelpers re-expose K2_GetRandomLocationInNavigableRadius
|
||||
// en BlueprintPure. Dependance PUBLIQUE : le .h expose ANavigationData et
|
||||
// UNavigationQueryFilter dans sa signature, et LifePlayEditorTools l'inclut.
|
||||
|
|
|
|||
Loading…
Reference in a new issue