fix(rendering): bound portal resource lifetime
Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
3971997689
commit
749e8ceeb1
225 changed files with 29107 additions and 3914 deletions
|
|
@ -171,8 +171,19 @@ public abstract class UiElement
|
|||
/// </summary>
|
||||
public bool IsEditControl { get; set; }
|
||||
|
||||
private int _zOrder;
|
||||
|
||||
/// <summary>Painter's-algorithm z-order within siblings. Higher = on top.</summary>
|
||||
public int ZOrder { get; set; }
|
||||
public int ZOrder
|
||||
{
|
||||
get => _zOrder;
|
||||
set
|
||||
{
|
||||
if (_zOrder == value) return;
|
||||
_zOrder = value;
|
||||
Parent?.InvalidateChildOrder();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Window opacity (0..1) multiplied into this element's and its
|
||||
/// descendants' background + sprite draws (text stays opaque). 1 = fully opaque.
|
||||
|
|
@ -269,6 +280,8 @@ public abstract class UiElement
|
|||
public UiElement? Parent { get; private set; }
|
||||
|
||||
private readonly List<UiElement> _children = new();
|
||||
private UiElement[]? _childrenBackToFront;
|
||||
private UiElement[]? _childrenFrontToBack;
|
||||
public IReadOnlyList<UiElement> Children => _children;
|
||||
|
||||
public virtual void AddChild(UiElement child)
|
||||
|
|
@ -276,6 +289,7 @@ public abstract class UiElement
|
|||
if (child.Parent is not null) child.Parent.RemoveChild(child);
|
||||
child.Parent = this;
|
||||
_children.Add(child);
|
||||
InvalidateChildOrder();
|
||||
}
|
||||
|
||||
public virtual bool RemoveChild(UiElement child)
|
||||
|
|
@ -284,9 +298,52 @@ public abstract class UiElement
|
|||
FindRoot()?.OnSubtreeRemoving(child);
|
||||
_children.Remove(child);
|
||||
child.Parent = null;
|
||||
InvalidateChildOrder();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable snapshots of the current sibling order. Retained UI mutation is
|
||||
/// render-thread-owned, so the arrays can be reused until membership or a
|
||||
/// child's Z-order changes. A traversal keeps its local array if a callback
|
||||
/// mutates the tree, preserving the prior snapshot semantics without a
|
||||
/// per-element allocation on every draw and overlay pass.
|
||||
/// </summary>
|
||||
internal UiElement[] ChildrenBackToFrontSnapshot()
|
||||
{
|
||||
if (_childrenBackToFront is not null)
|
||||
return _childrenBackToFront;
|
||||
|
||||
_childrenBackToFront = _children.ToArray();
|
||||
Array.Sort(
|
||||
_childrenBackToFront,
|
||||
static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
return _childrenBackToFront;
|
||||
}
|
||||
|
||||
internal UiElement[] ChildrenFrontToBackSnapshot()
|
||||
{
|
||||
if (_childrenFrontToBack is not null)
|
||||
return _childrenFrontToBack;
|
||||
|
||||
UiElement[] backToFront = ChildrenBackToFrontSnapshot();
|
||||
_childrenFrontToBack = new UiElement[backToFront.Length];
|
||||
for (int source = backToFront.Length - 1, destination = 0;
|
||||
source >= 0;
|
||||
source--, destination++)
|
||||
{
|
||||
_childrenFrontToBack[destination] = backToFront[source];
|
||||
}
|
||||
|
||||
return _childrenFrontToBack;
|
||||
}
|
||||
|
||||
private void InvalidateChildOrder()
|
||||
{
|
||||
_childrenBackToFront = null;
|
||||
_childrenFrontToBack = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if this widget draws its full appearance itself and REPRODUCES its dat
|
||||
/// sub-elements procedurally (3-slice caps, button labels, scroll arrows, popup
|
||||
|
|
@ -405,9 +462,7 @@ public abstract class UiElement
|
|||
ctx.PushClip(0f, 0f, Width, Height);
|
||||
try
|
||||
{
|
||||
// Avoid LINQ allocation by copying to a temp array and sorting.
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
UiElement[] ordered = ChildrenBackToFrontSnapshot();
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawSelfAndChildren(ctx);
|
||||
}
|
||||
|
|
@ -449,8 +504,7 @@ public abstract class UiElement
|
|||
ctx.PushClip(0f, 0f, Width, Height);
|
||||
try
|
||||
{
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
UiElement[] ordered = ChildrenBackToFrontSnapshot();
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawOverlays(ctx);
|
||||
}
|
||||
|
|
@ -496,8 +550,7 @@ public abstract class UiElement
|
|||
// only whether THIS element claims the hit.
|
||||
if (_children.Count > 0)
|
||||
{
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => b.ZOrder.CompareTo(a.ZOrder));
|
||||
UiElement[] ordered = ChildrenFrontToBackSnapshot();
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
{
|
||||
var c = ordered[i];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue