LifePlay-Revival/Docs/Migration/etat/U58/U7b/U7b-T1-CART.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

17 KiB
Raw Blame History

Fiche U7b-T1 — Niveau N3 — Modèle prévu lp-cart | utilisé claude-opus-5-5 — Agent lp-cart — Skills sk-protocol, lp-paths (préchargés aussi : ue-cpp-build, lp-commandlets)

U7b-T1 : cartographie de la capture des planches de la tournée Mesure (fenêtre minimisée, UE 5.8)

Date : 2026-10-09. Lecture seule : rien n'a été modifié, lancé ni commité. Addon = worktree D:\Game\LifePlay-Sandbox-5.8, branche ue5.8, HEAD d1efdd1, git status --porcelain vide. Moteur = D:\Game\Launcher\Engine\UE_5.8, 5.8.3, CL 58210709 (Engine/Build/Build.version), sources présentes. Dans ce document, E58 = D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime.

Écart à la fiche (méthode § 4) : aucun outil de recherche web (WebSearch / WebFetch) n'est disponible dans cette session. Les sources moteur 5.8 installées (source primaire, règle d'or de ue-cpp-build) les remplacent. Je ne cite aucune URL parce que je n'en ai consulté aucune. Si l'orchestrateur veut un recoupement par la documentation Epic, il faut une session qui a un accès web.

1. Où et comment la tournée capture aujourd'hui (établi)

1.1 Lancement

  • Pilote : Tools\anim\AnimPipeline.ps1, fonction Invoke-Sandbox (l. 300-356). Il écrit Saved\sandbox_mode.txt = Mesure et Saved\sandbox_tour.txt = vignettes planches quitter (l. 329-331), puis lance Tools\debug\LifePlay_interaction_sandbox.bat (l. 306 et 334, Start-Process cmd.exe ... -WindowStyle Hidden). Ensuite, il attend que le processus UnrealEditor se termine, avec un plafond de 40 min (l. 338-340).
  • Lanceur Tools\debug\LifePlay_interaction_sandbox.bat:9 : UnrealEditor.exe <uproject> "/Engine/Maps/Entry?game=/Script/LPSandbox.LPPairSandboxGameMode" -game -windowed -ResX=1600 -ResY=900 "-ini:Engine:[...]:GameInstanceClass=...". Le .bat ne transmet pas d'arguments supplémentaires (%* absent).
  • Le même .bat sert aussi à l'ouverture interactive : Tools\BuildMod.ps1:165 (Open-AnimTarget -Which sandbox) et README.md:53. Changer son comportement par défaut touche donc aussi l'usage manuel.

1.2 Capture (fichier Plugins\LPSandbox\Source\LPSandbox\LPSandboxMeasureTour.cpp)

  • Le mode de capture est unique : FScreenshotRequest::RequestScreenshot(<chemin>, false, false), c'est-à-dire sans interface et sans suffixe. Il y a 4 appels (grep -c = 4) :
    • l. 127 : passages automatiques (TickAuto) ;
    • l. 541 : planches (TickTour, bPlancheTour) ;
    • l. 561 : vignettes ;
    • l. 566 : tournée simple.
  • Les images ne sont pas écrites telles quelles. Elles passent par le délégué UGameViewportClient::OnScreenshotCaptured(), branché l. 427 (SavePlancheFrame) et l. 433 (SaveVignette), débranché l. 458 et 468 (4 occurrences, grep -c = 4). Ensuite :
    • SavePlancheFrame (l. 611-619) empile les TArray<FColor> ;
    • WritePlanche (l. 621-681) recadre le carré commun, ramène chaque image à 256 px, encode en JPEG qualité 85 et écrit <Clip>.jpg et <Clip>.json. Il trace PLANCHE ... (carre N px) à la l. 679.
  • Le cadrage se fait dans l'espace écran du viewport : ProjectWorldLocationToScreen des os (l. 533-539 pour les planches, l. 552-558 pour les vignettes), appliqué ensuite sur l'image reçue (l. 632-642, 581-591). Toute solution doit donc garder une image de la même taille que le viewport (1600×900), sinon le carré ne tombe plus au même endroit.
  • L'addon n'a aucune autre voie de capture : HighResShot, SceneCapture et ReadPixels n'apparaissent dans aucun fichier de Plugins\LPSandbox\Source (grep -rln, code 1). Aucune garde « fenêtre minimisée » n'existe non plus : IsWindowMinimized, RenderOffScreen et bAllowMinimize n'apparaissent ni dans les sources ni dans les outils de l'addon, ni dans Config du jeu 5.8.
  • LPSandboxMeasureTour.cpp est identique entre ue5.8 et la branche addon 5.5 lot/DC3-atelier (git diff --stat ue5.8 lot/DC3-atelier -- <fichier> : seuls le .bat et AnimPipeline.ps1 diffèrent).

1.3 Chemin moteur d'une capture (5.8)

  1. FViewport::Draw continue tant que SizeX > 0 && SizeY > 0 (E58\Engine\Private\UnrealClient.cpp:1789, test l. 1820). Pendant la minimisation, c'est la taille de FSceneViewport qui compte (1600×900), pas celle de la fenêtre.
  2. UGameViewportClient::ProcessScreenShots (E58\Engine\Private\GameViewportClient.cpp:2369) passe par la branche sans interface → GetViewportScreenShot (UnrealClient.cpp:2407-2416) → FRenderTarget::ReadPixels (UnrealClient.cpp:61-79). Comme le rectangle demandé est vide, il est remplacé par (0,0,GetSizeXY()), soit 1600×900. La lecture porte sur GetShaderResourceTexture().
  3. En rendu direct à la fenêtre, la cible du viewport est le back buffer Slate : Target = ViewportProvider->GetBackBufferResource() (E58\Engine\Private\Slate\SceneViewport.cpp:2289-2300).
  4. Les images arrivent ensuite dans le délégué (GameViewportClient.cpp:2543-2552).

2. Pourquoi la fenêtre minimisée plante

2.1 Chaîne établie (sources 5.8 et journal)

  1. Minimisation → WM_SIZE avec SIZE_MINIMIZED, zone client 0×0 → MessageHandler->OnSizeChanged(..., 0, 0, bWasMinimized) (E58\ApplicationCore\Private\Windows\WindowsApplication.cpp:3943-3966).
  2. FSlateApplication::OnSizeChanged → Renderer->RequestResize(Window, 0, 0) (E58\Slate\Private\Framework\Application\SlateApplication.cpp:6960-6970).
  3. FSlateRHIRenderer::RequestResize → FSlateViewportInfo::ResizeViewport (E58\SlateRHIRenderer\Private\SlateRHIRenderer.cpp:2114-2123). ClampExtent porte la taille à MIN_VIEWPORT_SIZE = 8 (l. 78, 287), puis RHIResizeViewport(8, 8) (l. 537, 576). Le back buffer BackBuffer0 mesure alors 8×8.
  4. Le viewport de jeu reste à 1600×900 et la capture demande donc une copie de 1600×900 depuis un back buffer de 8×8. FD3D12DynamicRHI::GetStagingTexture appelle CopyTextureRegionChecked (E58\D3D12RHI\Private\D3D12RenderTarget.cpp:472). Le contrôle RHI_BREADCRUMB_CHECK_SHIPPINGF(CopyBox.right <= SubresourceWidth) échoue (E58\D3D12RHI\Private\D3D12Texture.cpp:1551).
  5. Le journal de la 1re tentative U4-T6 (D:\Game\LifePlay_Backup_5.5\U4-T6\E4_quaternius_echec1.log) le confirme :
    • l. 6721-6722 : Source width out of bounds for: 'BackBuffer0' -> 'None' ... dimensions [8, 8, 1], copying from [0, 0, 0] to [1600, 900, 1] ;
    • l. 6739 : callstack GetStagingTexture ... D3D12RenderTarget.cpp:483 (attribution de ligne d'un appel sur plusieurs lignes, l. 472-483).

2.2 Pourquoi 5.5 ne plantait pas

  • Établi : en 5.5, GetStagingTexture appelle directement GraphicsCommandList()->CopyTextureRegion (UE_5.5\...\D3D12RenderTarget.cpp:472). Le texte « out of bounds » ne figure nulle part dans UE_5.5\...\D3D12Texture.cpp (grep -c = 0). Le contrôle de bornes est donc nouveau en 5.8.
  • Estimé : en 5.5, une fenêtre minimisée aurait produit une copie invalide sans erreur (images fausses ou noires) plutôt qu'un plantage. L'assertion 5.8 transforme une corruption silencieuse en arrêt net.
  • Estimé : une fenêtre seulement recouverte, ou un écran verrouillé, ne déclenche pas WM_SIZE/SIZE_MINIMIZED. Le back buffer garde alors sa taille et la capture ne plante pas. Seule la minimisation est en cause (non essayé).
  • Établi (relance T6-bis, U4-T6-VAL.md:110-120) : avec la fenêtre visible, il n'y a aucune assertion et les codes sont 0.

2.3 Pas de réglage moteur « rendre même minimisé »

  • grep -rli "ForceRenderingWhenMinimized|RenderWhenMinimized" sur E58 (*.cpp) ne trouve rien : ce CVar n'existe pas en 5.8.
  • tick.MinimizedSyncDrawToGPU (E58\Engine\Private\GameEngine.cpp:105-109) ne fait que synchroniser le GPU quand la fenêtre est cachée (l. 2057). Il ne règle pas la taille du back buffer.

3. Référence de non-régression : ce qui est comparable (établi)

  • Les pixels ne sont déjà pas identiques entre 5.5 et 5.8, fenêtre visible. Sur les 20 images de freeanims (U4-REF55\freeanims\sandbox et U4-T6\freeanims\sandbox), 0 sur 20 ont le même SHA-256. Écart de taille : planches -1,4 % à +2,5 %, vignettes +3,5 % à +19 % (sha256sum/stat, boucle sur les 30 fichiers). Le critère « pixels identiques » est donc hors d'atteinte quelle que soit l'option, y compris l'état actuel.
  • Trois points sont en revanche identiques :
    • les .json des planches (10/10, SHA-256) ;
    • les dimensions (20/20 : planches 6144×256, ou 3328×256 pour 13 images ; vignettes 512×512, lues dans l'en-tête par le script imgdims.py du bloc-notes, python -I) ;
    • surtout, la géométrie de recadrage : lignes VIGNETTE ... (carre N px a X,Y) et PLANCHE ... N images D s (carre N px), une fois le chemin et le nombre d'octets retirés : freeanims 20/20 et quaternius 86/86 identiques 5.5 / 5.8. Commande : grep -E "PLANCHE |VIGNETTE " sur U4-REF55\E4_<pack>.log et U4-T6\E4_<pack>.log, sed des chemins et des octets, puis diff, qui ne signale aucune différence.
  • Critère proposé pour U7b : code 0 ; 0 Assertion failed ; géométrie 106/106 identique à U4-REF55 ; .json des planches identiques ; dimensions identiques ; contrôle visuel d'une planche par pack. Cette géométrie dépend de la taille du viewport (1600×900) et de la pose : elle détecte tout changement de résolution ou de cadrage.

4. Options

Option Principe Points d'appel à changer Effort Risque Non-régression attendue
A. -RenderOffScreen (sans C++) Pas de fenêtre système : Slate rend dans une texture, donc pas de WM_SIZE ni de back buffer 8×8. Même chaîne de capture. .bat:9 : transmettre %*, ou un .bat dédié au pilote. AnimPipeline.ps1:334 : ajouter -RenderOffScreen. Libellé :317. Ne pas changer le défaut du .bat (BuildMod.ps1:165). XS, 2 à 5 lignes, 0 C++ Faible à moyen (voir 4.1) Géométrie identique attendue (même viewport, même lecture) : estimé. Pixels : variation déjà présente aujourd'hui.
B. SceneCapture2D → render target 1600×900 → ReadPixels Capture hors du back buffer : on lit la texture du render target (UnrealClient.cpp:61-79), quel que soit l'état de la fenêtre. LPSandboxMeasureTour.cpp : les 4 RequestScreenshot et les 4 branchements du délégué remplacés par un composant de capture qui copie la vue (position, rotation, FOV) du PlayerCameraManager. M à L, environ 100 à 150 lignes C++ ; compilation 5.8, plus 5.5 si le fichier reste commun Moyen à élevé Géométrie identique seulement si la taille et le FOV sont copiés exactement. Rendu différent : historique TAA/TSR, exposition auto, post-process, drapeaux d'affichage (SceneCaptureComponent.h:130-132, SceneCaptureComponent2D.h:174-192). Il faut une nouvelle référence visuelle.
C1. bAllowMinimize=False (sans C++) Retire le bouton de réduction : GetProjectSettingBool lit [/Script/EngineSettings.GeneralProjectSettings] de GGameIni (GameEngine.cpp:539-542, 558, 693) → pas de WS_MINIMIZEBOX (E58\ApplicationCore\Private\Windows\WindowsWindow.cpp:143-145). Surcharge possible en ligne de commande : -ini:Game:[/Script/EngineSettings.GeneralProjectSettings]:bAllowMinimize=False (syntaxe générique, E58\Core\Private\Misc\ConfigCacheIni.cpp:2322-2324). .bat:9 ou argument du pilote. XS Moyen : couverture incomplète. Win+D, Win+M, clic sur la barre des tâches ou réduction programmée restent possibles (incertain, non vérifié). Pixels et géométrie inchangés (même chemin).
C2. Garde dans la tournée (C++) Avant chaque RequestScreenshot, si GEngine->GameViewport->GetWindow()->IsWindowMinimized() (SWindow.h:758, GameViewportClient.h:362), alors Restore() (SWindow.h:928), journaliser, reculer l'étape (--TourStep, comme l. 516-519) et attendre. LPSandboxMeasureTour.cpp : un point commun avant les l. 127, 541, 561 et 566. S, environ 15 à 25 lignes Faible à moyen : la fenêtre reprend le premier plan. Une fenêtre réduite entre le tick et le rendu dans la même image : estimé impossible, puisque les messages sont traités en début d'image. Pixels et géométrie inchangés (même chemin), après au moins une image d'attente une fois la fenêtre restaurée.

4.1 Détail de l'option A (établi dans les sources 5.8, sauf mention)

  • Sous Windows, -RenderOffScreen remplace l'application Windows par l'application nulle (E58\ApplicationCore\Private\Windows\WindowsPlatformApplicationMisc.cpp:173-176). Cette voie est prévue sous Windows (Null\NullPlatformApplicationMisc.cpp:14-20, commentaire Epic « supported on Linux and Windows »).
  • La fenêtre est une FNullWindow et IsRenderingOffScreen() vaut true (Null\NullWindow.cpp:251-254). Slate crée alors une texture SlateOffScreenViewportTexture à la place d'une chaîne d'échange (SlateRHIRenderer.cpp:321-324, 340-353), et GetBackBufferResource renvoie cette texture (SlateRHIRenderer.cpp:471-474). Le viewport D3D12 n'a pas de chaîne d'échange (E58\D3D12RHI\Private\Windows\WindowsD3D12Viewport.cpp:34).
  • Il n'y a ni écran de démarrage (E58\Launch\Private\LaunchEngineLoop.cpp:3111) ni fenêtre système, donc ni minimisation ni WM_SIZE. Slate dessine quand même la fenêtre (SlateApplication.cpp:1249). La taille de la fenêtre n'est pas ajustée à la zone de travail (SceneViewport.cpp:1718-1720).
  • Risques à lever par l'essai décisif :
    • (a) taille réelle du viewport. La zone de travail de l'application nulle part de r.SetRes (Null\NullApplication.cpp:103-112). La config pose r.setres:1280x720 (E4_freeanims.log:523) avant que -ResX=1600 -ResY=900 ne s'applique. La géométrie identique prouvera que la taille est bien 1600×900.
    • (b) rythme des images : sans présentation à l'écran, il n'y a ni vsync ni limite d'images. Les attentes de la tournée sont en secondes (LoadWait 0,9 s, CameraWait 0,5 s, l. 206-207). La géométrie ne doit donc pas bouger, mais la convergence TAA peut changer les pixels (estimé).
    • (c) aucune fenêtre visible pendant la tournée : l'utilisateur ne la voit plus. Le pilote n'attend que le processus, donc c'est sans effet sur lui.
    • (d) l'application nulle ne reçoit pas de clavier. C'est sans effet : la tournée est pilotée par sandbox_tour.txt (l. 290-311) et se ferme par RequestExit (l. 356).

5. Recommandation

  1. A d'abord, réservée au pilote. Le .bat transmet %*, ou un .bat dédié est créé, et AnimPipeline.ps1:334 passe -RenderOffScreen. Le défaut interactif reste inchangé. Cette option supprime la cause (pas de back buffer de fenêtre), sans C++ et sans nouvelle compilation, sur les deux moteurs.
  2. Si A échoue (géométrie différente, plantage, viewport ≠ 1600×900) : C2, une garde de quelques lignes sur le même chemin de capture, donc une non-régression assurée par construction.
  3. B en dernier recours seulement : c'est la voie la plus coûteuse, elle change le rendu et impose une nouvelle référence visuelle.
  4. C1 n'est utile qu'en complément : elle ne couvre pas tous les moyens de réduire une fenêtre.

6. Essai décisif (à faire dans le lot IMPL/VAL, pas ici)

  1. Pré-essai court, d'une à deux minutes. Reproduire Invoke-Sandbox à la main pour un seul clip : sandbox_pack.ini du pack freeanims, sandbox_mode.txt = Mesure, sandbox_tour.txt = vignettes planches quitter AS_RightLegKick2 (liste partielle autorisée, l. 294-303). Lancer avec -RenderOffScreen. Attendu : code 0, 0 Assertion failed, et les lignes VIGNETTE ... (carre 695 px a 621,173) et PLANCHE ... 13 images 1.10 s ... (carre 724 px), identiques à U4-REF55.
  2. Essai complet : AnimPipeline.ps1 -Pack freeanims -From 5 -To 5 (446 s en 5.8). Comparer la géométrie (20/20), les .json des planches (10/10) et les dimensions (20/20) à U4-REF55\freeanims, avec le même diff qu'au § 3. Puis quaternius (86/86, environ 1210 s).
  3. Essai contraire, si C2 est retenu : lancer fenêtre visible, la réduire pendant les planches. Attendu : ligne de journal « fenêtre restaurée », code 0, géométrie identique. Sans la garde, le plantage D3D12Texture.cpp:1551 doit se reproduire : c'est la preuve par l'échec, à documenter avant le correctif.

7. Preuves, incertitudes

  • Établi : §§ 1, 2.1, 2.2 (premier point), 2.3, 3 et 4.1 (mécanique), avec les lignes et commandes citées.
  • Estimé :
    • comportement 5.5 en fenêtre minimisée (corruption silencieuse) ;
    • fenêtre recouverte ou écran verrouillé sans effet ;
    • géométrie conservée sous -RenderOffScreen ;
    • absence de course dans C2.
  • Incertain :
    • taille effective du viewport sous -RenderOffScreen avec r.setres:1280x720 en config (essai 6.1) ;
    • couverture de C1 face à Win+D / Win+M ;
    • déterminisme des pixels d'une exécution 5.8 à l'autre (aucune paire 5.8/5.8 conservée) ;
    • recoupement par la documentation Epic, non fait faute d'outil web.