fix(ui): allegiance page — hoist per-frame LinesProvider allocation, document the coarser empty-state gate
Mechanism SHOULD-FIX 2 / blast SHOULD-FIX 4 (same finding, both reviews): SocialAllegiancePageController.Tick() closed over a local `lines` on every call — `() => lines` allocated a display class plus a delegate on EVERY frame, unconditionally, at ~2,000 allocations/second at the profile's measured FPS, in a hot loop the Modern Runtime slices spent whole commits driving to 0 B/frame. Hoisted two static readonly Func<IReadOnlyList<UiText.Line>> providers (BlankLineProvider/ NoLinesProvider); Tick() now assigns the cached delegate reference — zero allocation while idle or active. Mechanism SHOULD-FIX 7: FA3's empty-state gate is coarser than the retail mechanism it is contracted against — gmAllegianceUI::UpdateMonarchData @0x00491B40 hides the monarch/patron blocks per-relationship (monarch block also hides when the monarch IS the viewer; patron block on the analogous test), while this shell gates both blocks on the single HasProfile flag. Not a MUST-FIX for FA3 (MF-2 in the same review means HasProfile is effectively always false for the whole FA3 gate, so nothing wrong is visible during this slice's own gate) — recorded instead as an explicit FA5 acceptance line in the plan's FA5 row so the gap cannot be silently lost, plus a class-doc note that FA5's real monarch/patron population must also change Tick()'s unconditional LinesProvider reassignment in the same commit or its content will be overwritten the next frame. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9afa05b563
commit
35c40a9b56
2 changed files with 29 additions and 4 deletions
|
|
@ -34,6 +34,20 @@ namespace AcDream.App.UI.Layout;
|
|||
/// picking the wrong instance (§6 DISCIPLINE, the campaign-OP
|
||||
/// <c>0x10000211</c>-in-two-layouts lesson).
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// <b>FA5 scope note (fix-round mechanism SF-7).</b> This shell's gate is
|
||||
/// coarser than retail's: <c>gmAllegianceUI::UpdateMonarchData
|
||||
/// @0x00491B40</c> hides the monarch block when there is no monarch OR the
|
||||
/// monarch IS the viewer, and hides the patron block on the analogous
|
||||
/// per-relationship test — this controller instead gates BOTH blocks on
|
||||
/// the single <see cref="RuntimeAllegianceSnapshot.HasProfile"/> flag. The
|
||||
/// plan's FA5 row records the two per-relationship acceptance lines FA5
|
||||
/// owes. Also note for whoever lands FA5: <see cref="Tick"/> reassigns
|
||||
/// <see cref="UiText.LinesProvider"/> UNCONDITIONALLY every frame — FA5's
|
||||
/// real monarch/patron name population must change this method at the
|
||||
/// same time, or its content will be overwritten on the very next frame.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class SocialAllegiancePageController
|
||||
{
|
||||
|
|
@ -42,6 +56,17 @@ public sealed class SocialAllegiancePageController
|
|||
|
||||
private static readonly IReadOnlyList<UiText.Line> NoLines = [];
|
||||
|
||||
/// <summary>
|
||||
/// Fix-round mechanism SF-2 / blast SF-4: hoisted <see cref="UiText.LinesProvider"/>
|
||||
/// delegates. <see cref="Tick"/> runs every frame regardless of panel
|
||||
/// visibility (see class doc); the original <c>() => lines</c> closure
|
||||
/// captured a local and allocated a display class PLUS a delegate on
|
||||
/// every call. These two cached delegates make the per-frame
|
||||
/// reassignment a plain field write — zero allocation while idle.
|
||||
/// </summary>
|
||||
private static readonly Func<IReadOnlyList<UiText.Line>> BlankLineProvider = () => BlankLine;
|
||||
private static readonly Func<IReadOnlyList<UiText.Line>> NoLinesProvider = () => NoLines;
|
||||
|
||||
private readonly UiElement _monarchField;
|
||||
private readonly UiElement _patronField;
|
||||
private readonly UiText? _monarchName;
|
||||
|
|
@ -105,8 +130,8 @@ public sealed class SocialAllegiancePageController
|
|||
_monarchField.Visible = hasProfile;
|
||||
_patronField.Visible = hasProfile;
|
||||
|
||||
IReadOnlyList<UiText.Line> lines = hasProfile ? NoLines : BlankLine;
|
||||
if (_monarchName is not null) _monarchName.LinesProvider = () => lines;
|
||||
if (_patronName is not null) _patronName.LinesProvider = () => lines;
|
||||
Func<IReadOnlyList<UiText.Line>> provider = hasProfile ? NoLinesProvider : BlankLineProvider;
|
||||
if (_monarchName is not null) _monarchName.LinesProvider = provider;
|
||||
if (_patronName is not null) _patronName.LinesProvider = provider;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue