fix(animation): harden presentation phase handoffs

Own every borrowed PartArray frame across callbacks, version presentation state across appearance rebinding, reject recursive phase consumption, and preserve static MotionDone when only a stale visual pose is discarded.
This commit is contained in:
Erik 2026-07-21 10:01:25 +02:00
parent a2fd61684a
commit 833520253a
10 changed files with 657 additions and 140 deletions

View file

@ -34,6 +34,8 @@ internal sealed class LiveEntityAnimationState : ILiveEntityAnimationRuntime
public bool SequenceAdvancedBeforeAnimationPass;
public readonly Frame RootMotionScratch = new();
public readonly MotionDeltaFrame RootMotionDeltaScratch = new();
public readonly List<PartTransform> SequenceFramesScratch = new();
public readonly List<PartTransform> ScheduleFramesScratch = new();
/// <summary>Reusable compact drawable channel, consumed before draw.</summary>
public readonly List<MeshRef> MeshRefsScratch = new();
@ -45,16 +47,40 @@ internal sealed class LiveEntityAnimationState : ILiveEntityAnimationRuntime
/// </summary>
public readonly List<Matrix4x4> VisualPartPosesScratch = new();
public bool PresentationPosesInitialized;
public ulong PresentationRevision { get; private set; } = 1UL;
public double LastSequenceDiagnosticTime;
public double LastPartDiagnosticTime;
public void InvalidatePresentationPoses()
{
PresentationRevision++;
if (PresentationRevision == 0UL)
PresentationRevision++;
PresentationPosesInitialized = false;
VisualPartPosesScratch.Clear();
EffectPartPosesScratch.Clear();
}
public IReadOnlyList<PartTransform> CaptureSequenceFrames(
IReadOnlyList<PartTransform> source)
{
ArgumentNullException.ThrowIfNull(source);
SequenceFramesScratch.Clear();
for (int i = 0; i < source.Count; i++)
SequenceFramesScratch.Add(source[i]);
return SequenceFramesScratch;
}
public IReadOnlyList<PartTransform> CaptureScheduleFrames(
IReadOnlyList<PartTransform> source)
{
ArgumentNullException.ThrowIfNull(source);
ScheduleFramesScratch.Clear();
for (int i = 0; i < source.Count; i++)
ScheduleFramesScratch.Add(source[i]);
return ScheduleFramesScratch;
}
}
internal readonly record struct LiveAnimationPartTemplate(