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
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:
parent
9deb1a28cf
commit
ca4bae776c
3 changed files with 116 additions and 0 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
75
src/AcDream.App/Rendering/PlayerPresentationProbe.cs
Normal file
75
src/AcDream.App/Rendering/PlayerPresentationProbe.cs
Normal 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=<path></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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue