docs #429 + probe: frame stalls are pack-independent (~1.7/s both arms); the pack converts them into visible player jumps (59 vs 3); prediction-snap theory dead
All checks were successful
CI / linux-portable (push) Successful in 3m30s
CI / windows-gate (push) Successful in 6m22s
CI / release (push) Successful in 2m15s

Adds the TEMPORARY PlayerPresentationProbe (ACDREAM_PROBE_PLAYER_PRESENT)
per the #429 apparatus plan and records the measured two-arm evidence in
the issue. Probe strips with the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-23 21:21:04 +02:00
parent 9deb1a28cf
commit ca4bae776c
3 changed files with 116 additions and 0 deletions

View file

@ -115,6 +115,39 @@ ACE's >=800 misread), accumulating drift that the AutoPos/correction loop
snaps back on its cadence. Walking (speed 1.0 both sides) and strafing snaps back on its cadence. Walking (speed 1.0 both sides) and strafing
accumulate little or no drift — matching the report exactly. accumulate little or no drift — matching the report exactly.
**MEASURED 2026-08-23 (same session) — the story inverted twice and is now
pinned by data.** A per-frame probe (`PlayerPresentationProbe`,
`ACDREAM_PROBE_PLAYER_PRESENT=<csv>`, TEMPORARY, wired in
`WorldRenderFrameBuilder`/`RuntimeWorldFrameEnvironmentPreparation`)
captured `(t, camera, presented player position)` per frame over ~50 s
of running per arm (`artifacts/owner-gate/player-present-429-packON.csv`
/ `-packoff.csv`, ~270 FPS baseline, median frame 3.7 ms):
- Frame stalls of 15-25 ms (max ~230-260 ms) occur ~1.7/s while moving in
BOTH arms — pack ON 88, pack OFF 83 — the Atmospheric pack does NOT
cause the stalls. The uncapped run reproducing the hitch had already
killed the #235 capped-alias theory.
- The pack changes what a stall LOOKS like: with pack ON, 59 of the 88
long frames carry a 3x-5x player-position jump (the visible hitch);
with pack OFF only 3 of 83 do — the same stall lands at a pipeline
phase where the presented position has not advanced, so the player
stays visually smooth and the owner never perceived it.
- Right after ON-arm stall clusters, normal-length frames show ~zero
player motion (catch-up artifacts). The prediction/AutoPos-correction
hypothesis is DEAD: position advances exactly proportionally to
elapsed time through the stalls; no snap-back is present in the data.
**Two separated defects:**
1. BASE CLIENT: periodic 15-25 ms frame stalls (~1.7/s while moving).
GC exonerated (Gen0 at 6.3 s cadence, gen1/2 zero). Suspects:
streaming publication/upload bursts on movement, present-path stalls.
Instrument with a per-stall stack/phase capture, not more theories.
2. PACK FRAME GRAPH: its ordering shifts the stall's position relative
to the physics commit/presented-position sampling, converting silent
stalls into visible player jumps. Establish where the ON-arm long
frames spend their extra time (the pack's CPU stage profiler names
stages) and where the player position is sampled relative to it.
**Next probes (in order):** **Next probes (in order):**
1. `ACDREAM_DUMP_MOTION=1` + a temporary inbound-position log for the 1. `ACDREAM_DUMP_MOTION=1` + a temporary inbound-position log for the
LOCAL guid: does ACE send position sets for the local player every LOCAL guid: does ACE send position sets for the local player every

View file

@ -0,0 +1,75 @@
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
namespace AcDream.App.Rendering;
/// <summary>
/// TEMPORARY #429 apparatus. One CSV row per rendered frame:
/// <c>seconds,camX,camY,camZ,playerX,playerY,playerZ</c>, where seconds is a
/// Stopwatch-derived monotonic time and player is the frame's
/// <c>PlayerViewPosition</c> — the presented local-player position driving
/// lighting/visibility this frame. Off unless
/// <c>ACDREAM_PROBE_PLAYER_PRESENT=&lt;path&gt;</c> is set (one null check per
/// frame). Analysis: a hitch frame shows the player's per-frame delta
/// collapsing to ~0 or doubling; whether the SAME frame's dt is smooth
/// separates a presentation-phase bug (pack frame graph sampling a stale
/// snapshot) from genuine frame-pacing spikes. Strip with the #429 fix.
/// </summary>
internal sealed class PlayerPresentationProbe : IDisposable
{
private readonly StreamWriter _writer;
private readonly long _startTimestamp = Stopwatch.GetTimestamp();
private int _linesSinceFlush;
private PlayerPresentationProbe(StreamWriter writer)
{
_writer = writer;
_writer.WriteLine("seconds,camX,camY,camZ,playerX,playerY,playerZ");
}
internal static PlayerPresentationProbe? CreateFromEnvironment()
{
string? path = Environment.GetEnvironmentVariable("ACDREAM_PROBE_PLAYER_PRESENT");
if (string.IsNullOrWhiteSpace(path))
return null;
try
{
return new PlayerPresentationProbe(new StreamWriter(path, append: false));
}
catch (Exception ex)
{
Console.Error.WriteLine(
$"[player-present] probe file '{path}' could not be opened: {ex.Message}");
return null;
}
}
internal void Observe(in Vector3 cameraPosition, in Vector3 playerViewPosition)
{
double seconds = (Stopwatch.GetTimestamp() - _startTimestamp)
/ (double)Stopwatch.Frequency;
_writer.WriteLine(string.Create(
CultureInfo.InvariantCulture,
$"{seconds:F6},{cameraPosition.X:F4},{cameraPosition.Y:F4},{cameraPosition.Z:F4},{playerViewPosition.X:F4},{playerViewPosition.Y:F4},{playerViewPosition.Z:F4}"));
if (++_linesSinceFlush >= 240)
{
_linesSinceFlush = 0;
_writer.Flush();
}
}
public void Dispose()
{
try
{
_writer.Flush();
_writer.Dispose();
}
catch
{
// A probe must never turn teardown fallible.
}
}
}

View file

@ -450,6 +450,10 @@ internal sealed class RuntimeWorldFrameEnvironmentPreparation
private readonly HashSet<uint> _visibleCells = []; private readonly HashSet<uint> _visibleCells = [];
private bool _visibleCellsValid; private bool _visibleCellsValid;
/// <summary>TEMPORARY #429 — see <see cref="PlayerPresentationProbe"/>.</summary>
private readonly PlayerPresentationProbe? _playerPresentProbe =
PlayerPresentationProbe.CreateFromEnvironment();
public RuntimeWorldFrameEnvironmentPreparation( public RuntimeWorldFrameEnvironmentPreparation(
RuntimeOptions options, RuntimeOptions options,
WorldTimeService worldTime, WorldTimeService worldTime,
@ -485,6 +489,10 @@ internal sealed class RuntimeWorldFrameEnvironmentPreparation
activeDayGroup, activeDayGroup,
camera.Position); camera.Position);
// TEMPORARY #429 apparatus — one CSV row per frame; off unless
// ACDREAM_PROBE_PLAYER_PRESENT names a file. Strip with the fix.
_playerPresentProbe?.Observe(camera.Position, roots.PlayerViewPosition);
UpdateSunFromSky(foundation.Sky, roots.PlayerInsideCell); UpdateSunFromSky(foundation.Sky, roots.PlayerInsideCell);
_lighting.UpdateViewerLight(roots.PlayerViewPosition); _lighting.UpdateViewerLight(roots.PlayerViewPosition);
_lighting.Tick(camera.Position); _lighting.Tick(camera.Position);