Harden movie playback lifecycle and diagnostics

This commit is contained in:
gamer147
2026-07-22 09:44:57 -04:00
parent 5b66cb2cd2
commit 740e10c550
25 changed files with 1145 additions and 147 deletions

View File

@@ -31,6 +31,22 @@ public readonly record struct NumericGlyphStyle(int SurfaceSlot, int AtlasX, int
public bool Registered => SurfaceSlot != 0;
}
public sealed record BlockingGfxObjectDiagnostic(
long Handle, int SourceSlot, long StartMs, long ControlFlags,
bool ColorEnabled, long ColorDelayMs, long ColorDurationMs,
bool ScaleEnabled, long ScaleDelayMs, long ScaleDurationMs,
bool RotationEnabled, long RotationDelayMs, long RotationDurationMs,
bool TranslationEnabled, long TranslationDelayMs, long TranslationDurationMs);
public sealed record GfxDiagnosticSnapshot(
long NowMs, bool HasActiveTimedPresentation, int ObjectCount, int VisibleObjectCount,
int ActiveSurfaceTransitionCount, long AnimationServiceFlags,
long AnimClockDurationTicks, long AnimClockGeneration,
uint PreviousFrameTimeMilliseconds, uint CurrentFrameTimeMilliseconds,
long RangeTransformFirst, long RangeTransformCount,
BlockingGfxObjectDiagnostic? BlockingRangeTransform,
IReadOnlyList<BlockingGfxObjectDiagnostic> BlockingObjects);
/// <summary>A renderable view of one visible gfx object — the host composites these in ascending-handle order
/// (= the engine's z-order) each frame. Built by <see cref="GfxState.SnapshotVisibleObjects"/>; the surface
/// resId/colorkey are resolved from the object's live source slot at snapshot time (see docs/engine-re.md,
@@ -545,6 +561,51 @@ public sealed class GfxState
o.RotationChannelEnabled || o.TranslationEnabled));
}
/// <summary>Observe-only state for a runtime stall capture. It identifies the exact finite channels
/// which can keep the host's op-0x21c presentation wait active.</summary>
public GfxDiagnosticSnapshot CaptureDiagnosticSnapshot(long nowMs)
{
lock (_lock)
{
BlockingGfxObjectDiagnostic? range = HasBlockingChannels(_rangeTransform)
? DescribeBlockingObject(-1, _rangeTransform)
: null;
var objects = _objects
.Where(pair => pair.Value.Visible
&& (pair.Value.OneShotAnimationControlFlags & 1) == 0
&& HasBlockingChannels(pair.Value))
.OrderBy(pair => pair.Key)
.Select(pair => DescribeBlockingObject(pair.Key, pair.Value))
.ToArray();
return new GfxDiagnosticSnapshot(
nowMs,
_surfaceTransitions.Values.Any(t => TransitionProgress(t, nowMs) < 1.0)
|| range != null || objects.Length != 0,
_objects.Count,
_objects.Values.Count(o => o.Visible),
_surfaceTransitions.Values.Count(t => TransitionProgress(t, nowMs) < 1.0),
AnimationServiceFlags,
AnimClockDurationTicks,
AnimClockGeneration,
PreviousFrameTimeMilliseconds,
CurrentFrameTimeMilliseconds,
_rangeTransformFirst,
_rangeTransformCount,
range,
objects);
}
}
private static bool HasBlockingChannels(GfxObject o)
=> o.OneShotColorEnabled || o.ScaleEnabled || o.RotationChannelEnabled || o.TranslationEnabled;
private static BlockingGfxObjectDiagnostic DescribeBlockingObject(long handle, GfxObject o)
=> new(handle, o.SourceSlot, o.OneShotStartMs, o.OneShotAnimationControlFlags,
o.OneShotColorEnabled, o.ColorDelayMs, o.ColorDurationMs,
o.ScaleEnabled, o.ScaleDelayMs, o.ScaleDurationMs,
o.RotationChannelEnabled, o.RotationDelayMs, o.RotationDurationMs,
o.TranslationEnabled, o.TranslationDelayMs, o.TranslationDurationMs);
/// <summary>Op 0x242: replace the retained object's animation-control word. Native bit 0 makes its
/// finite one-shot channels nonblocking and immune to op 0x243 forced completion.</summary>
public void SetOneShotAnimationControl(long handle, long flags)