wip(assembler): round S11 -- bounded spawn apply budget: in-game full set 7.1 s -> 6.0 s
Startup apply mode on by default (12 meshes / 14 ms per frame, ends when the initial set and walkable ring are ready, 30 s cap); normal play keeps the authored budget. Backlog peak 252 -> 10, LOD8 apply wait 1.1 s -> 4 ms. Ring 0.49 s, LOD0 1.40 s, 40 m/s ring 75/75. Remaining floor: frame-paced admission (~8.3 submits/frame, 8 running workers max). No geometry change. Report: Standalone/Research/WorldAssemblerM3/Round15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
b90d63d0f7
commit
dd9bfd3217
6 files changed
+240
-7
No files matched your search
@@ -1679,6 +1679,34 @@ coverage gates still pass at their existing tolerances. BuildHost actor startup,
|
|||||||
matrix, W1/W8/W12 Standalone hashes, the 40 m/s support-ring trace and the S10/S9 captures are
|
matrix, W1/W8/W12 Standalone hashes, the 40 m/s support-ring trace and the S10/S9 captures are
|
||||||
recorded in [`Standalone/Research/WorldAssemblerM3/Round14/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round14/REPORT.md).
|
recorded in [`Standalone/Research/WorldAssemblerM3/Round14/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round14/REPORT.md).
|
||||||
|
|
||||||
|
### 8.32 Bounded spawn-phase mesh applies — World Assembler S11 / M3 Round 15
|
||||||
|
|
||||||
|
The shared `AVoxelWorld::ProcessPendingChunks` startup budget now defaults on for the initial desired
|
||||||
|
set: up to 12 visible mesh applies or 14 ms in one frame, with a 30 second ceiling. It ends as soon
|
||||||
|
as the first desired set and stationary walkable ring are ready, the desired epoch changes, an
|
||||||
|
explicit per-step apply override is supplied, or the ceiling expires. Normal play uses the authored
|
||||||
|
`MaxMeshAppliesPerFrame` and `MaxMeshApplyMilliseconds` values. Set
|
||||||
|
`voxel.StreamingStartupApplyBoost=0` for the matched authored-budget control. `startup_apply_config`
|
||||||
|
records the actor-read CVar and both budgets; `startup_apply_budget` records when the bounded phase
|
||||||
|
starts and ends.
|
||||||
|
|
||||||
|
On the matched 25 cm SurfaceWorld trace, both fresh processes handled 2,578 initial tiles. With the
|
||||||
|
authored 4-mesh/2 ms budget, readiness was 7.118 s, the nine-tile walkable ring was ready at 0.495 s,
|
||||||
|
and full LOD0 at 1.444 s. The four-mesh limit hit 305 of 399 apply frames; result backlog peaked at
|
||||||
|
252/256 and result-wait p95/max was 847/1,119 ms. The last LOD8 worker ended at 6.068 s, but its
|
||||||
|
result applied at 7.182 s. With the shared startup budget enabled, readiness was 5.960 s, the ring
|
||||||
|
was ready at 0.486 s, and full LOD0 at 1.403 s. No apply-count frame hit its limit; backlog peaked
|
||||||
|
at 10 and result-wait p95/max fell to 16.6/19.3 ms. The last LOD8 worker/apply times were 6.015/6.019 s.
|
||||||
|
|
||||||
|
The remaining worker-generation span is frame-paced: 2,578 requests arrived over 310 submission
|
||||||
|
passes (8.32 per pass) in 5.58 s. The trace integrates 4.8 busy workers out of 10, while sampled
|
||||||
|
active task guards averaged 9.9/10 and sampled running workers peaked at 8. The LOD4 group used the
|
||||||
|
most worker time (6.590 s) but finished at 4.276 s; the final LOD8 group applied at 6.019 s. The 60
|
||||||
|
initial RMC body updates had an 18.4 ms p95 submit-to-body-update interval, and the walkable ring was
|
||||||
|
ready well before that far-LOD tail. Geometry and collision arithmetic do not change, so recipe
|
||||||
|
versions stay unchanged.
|
||||||
|
See [`Standalone/Research/WorldAssemblerM3/Round15/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round15/REPORT.md).
|
||||||
|
|
||||||
## 9. Multiplayer model (listen-server first, dedicated-friendly)
|
## 9. Multiplayer model (listen-server first, dedicated-friendly)
|
||||||
|
|
||||||
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
|
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
|
||||||
|
|||||||
+1
-1
@@ -369,7 +369,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error**
|
|||||||
| `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: optional near digs, then repeats underfoot digs and samples `Generator->GetDensityAt` immediately after each edit, on its first subsequent simulated frame, and on every following observation frame. `-voxel.TestDigMotionFieldOverride=1` is a measurement-only prototype that clears `CurrentFloor` and switches CMC to falling when the sampled post-edit density is air; it is inactive by default. The test counts false support, false air and downward velocity beyond one gravity step. `-voxel.TestDigMotionUnderfootDigCount` is clamped to 1–100; `-voxel.TestDigMotionUnderfootIntervalSeconds=0` performs per-frame rapid edits. The accompanying gap audit is scoped to the dig session. |
|
| `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: optional near digs, then repeats underfoot digs and samples `Generator->GetDensityAt` immediately after each edit, on its first subsequent simulated frame, and on every following observation frame. `-voxel.TestDigMotionFieldOverride=1` is a measurement-only prototype that clears `CurrentFloor` and switches CMC to falling when the sampled post-edit density is air; it is inactive by default. The test counts false support, false air and downward velocity beyond one gravity step. `-voxel.TestDigMotionUnderfootDigCount` is clamped to 1–100; `-voxel.TestDigMotionUnderfootIntervalSeconds=0` performs per-frame rapid edits. The accompanying gap audit is scoped to the dig session. |
|
||||||
| `MaybeFinishHeadlessStreamingTest` | — | The existing `voxel.TestExitSeconds` diagnostic emits `VoxelForgeStreamingExitDiag` at most every 30 elapsed seconds, including threshold and early-return test flags; no output when the timer is unconfigured. |
|
| `MaybeFinishHeadlessStreamingTest` | — | The existing `voxel.TestExitSeconds` diagnostic emits `VoxelForgeStreamingExitDiag` at most every 30 elapsed seconds, including threshold and early-return test flags; no output when the timer is unconfigured. |
|
||||||
| `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. |
|
| `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. |
|
||||||
| `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue` under the per-frame budget; reads the per-request cancellation token before removing its map entry, traces stale/obsolete/aborted discards with epoch and request ordinal, then applies each live result via `ApplyTileResult`. Collision profiling timestamps result arrival separately from game-thread apply wait. Step 1's `voxel.Step1PrequeueVisibleLOD0` still releases through this drain and emits `VFStep1UploadCorpusReleased`. |
|
| `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue`; applies under the authored 4-mesh/2 ms budget during normal play and the bounded shared startup budget (12 meshes/14 ms) until the initial desired set and walkable ring are ready. `voxel.StreamingStartupApplyBoost=0` restores the authored budget. `startup_apply_config` traces the actor-read CVar and both budgets; `startup_apply_budget` records activation/end. Reads per-request cancellation before removing its map entry, traces stale/obsolete/aborted discards with epoch and ordinal, then applies via `ApplyTileResult`. Step 1 prequeue still releases here and emits `VFStep1UploadCorpusReleased`. |
|
||||||
| `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). |
|
| `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). |
|
||||||
| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. |
|
| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=<us>`, commandlet `-samplestacks[=us]`. |
|
||||||
| Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). |
|
| Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). |
|
||||||
|
|||||||
@@ -267,11 +267,11 @@ namespace
|
|||||||
GVoxelForgeStep1MaxMeshApplies,
|
GVoxelForgeStep1MaxMeshApplies,
|
||||||
TEXT("Step 1 measurement override: 0 uses the asset MaxMeshAppliesPerFrame, otherwise caps visible applies per frame."));
|
TEXT("Step 1 measurement override: 0 uses the asset MaxMeshAppliesPerFrame, otherwise caps visible applies per frame."));
|
||||||
|
|
||||||
int32 GVoxelForgeStreamingStartupApplyBoost = 0;
|
int32 GVoxelForgeStreamingStartupApplyBoost = 1;
|
||||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingStartupApplyBoost(
|
FAutoConsoleVariableRef CVarVoxelForgeStreamingStartupApplyBoost(
|
||||||
TEXT("voxel.StreamingStartupApplyBoost"),
|
TEXT("voxel.StreamingStartupApplyBoost"),
|
||||||
GVoxelForgeStreamingStartupApplyBoost,
|
GVoxelForgeStreamingStartupApplyBoost,
|
||||||
TEXT("Opt-in cold-view upload experiment (default off): 1 allows a 14 ms / 12-mesh budget until the initial desired set and walkable ring are ready; 0 uses the authored per-frame budget."));
|
TEXT("Bounded cold-view upload budget (default on): 1 allows a 14 ms / 12-mesh budget until the initial desired set and walkable ring are ready; 0 uses the authored per-frame budget."));
|
||||||
constexpr int32 StreamingStartupMaxMeshApplies = 12;
|
constexpr int32 StreamingStartupMaxMeshApplies = 12;
|
||||||
constexpr float StreamingStartupApplyBudgetMilliseconds = 14.0f;
|
constexpr float StreamingStartupApplyBudgetMilliseconds = 14.0f;
|
||||||
constexpr double StreamingStartupApplyMaxSeconds = 30.0;
|
constexpr double StreamingStartupApplyMaxSeconds = 30.0;
|
||||||
@@ -9259,6 +9259,25 @@ void AVoxelWorld::ProcessPendingChunks()
|
|||||||
// Budget limits how many VISIBLE mesh applies we do per frame (GPU upload cost).
|
// Budget limits how many VISIBLE mesh applies we do per frame (GPU upload cost).
|
||||||
// Empty meshes and stale results are free to drain — don't count them.
|
// Empty meshes and stale results are free to drain — don't count them.
|
||||||
const bool bProfileApply = GVoxelForgeProfileTileGeneration != 0;
|
const bool bProfileApply = GVoxelForgeProfileTileGeneration != 0;
|
||||||
|
if (!bStartupApplyConfigTraced)
|
||||||
|
{
|
||||||
|
bStartupApplyConfigTraced = true;
|
||||||
|
if (VoxelForgeStartupTrace::IsActive())
|
||||||
|
{
|
||||||
|
VoxelForgeStartupTrace::RecordEvent(TEXT("startup_apply_config"), FString::Printf(
|
||||||
|
TEXT("\"startup_apply_boost_cvar\":%d,\"step1_max_applies\":%d,"
|
||||||
|
"\"asset_max_applies\":%d,\"asset_budget_ms\":%.3f,"
|
||||||
|
"\"boost_max_applies\":%d,\"boost_budget_ms\":%.3f,"
|
||||||
|
"\"boost_max_duration_s\":%.3f"),
|
||||||
|
GVoxelForgeStreamingStartupApplyBoost,
|
||||||
|
GVoxelForgeStep1MaxMeshApplies,
|
||||||
|
Settings ? Settings->MaxMeshAppliesPerFrame : 4,
|
||||||
|
Settings ? FMath::Max(Settings->MaxMeshApplyMilliseconds, 0.0f) : 2.0f,
|
||||||
|
StreamingStartupMaxMeshApplies,
|
||||||
|
StreamingStartupApplyBudgetMilliseconds,
|
||||||
|
StreamingStartupApplyMaxSeconds));
|
||||||
|
}
|
||||||
|
}
|
||||||
const uint64 ApplyFrameStartCycles = FPlatformTime::Cycles64();
|
const uint64 ApplyFrameStartCycles = FPlatformTime::Cycles64();
|
||||||
const double FrameSeconds = LastApplyFrameCycles != 0
|
const double FrameSeconds = LastApplyFrameCycles != 0
|
||||||
&& ApplyFrameStartCycles >= LastApplyFrameCycles
|
&& ApplyFrameStartCycles >= LastApplyFrameCycles
|
||||||
|
|||||||
@@ -1225,6 +1225,8 @@ public:
|
|||||||
bool bSampleStacksStarted = false;
|
bool bSampleStacksStarted = false;
|
||||||
bool bStartupApplyBoostStarted = false;
|
bool bStartupApplyBoostStarted = false;
|
||||||
bool bStartupApplyBoostEnded = false;
|
bool bStartupApplyBoostEnded = false;
|
||||||
|
// Trace the resolved startup budget once per world. / Tracer une fois le budget de demarrage resolu par monde.
|
||||||
|
bool bStartupApplyConfigTraced = false;
|
||||||
uint64 LastApplyFrameCycles = 0;
|
uint64 LastApplyFrameCycles = 0;
|
||||||
float StartupTraceSpeedCmPerSecond = 0.0f;
|
float StartupTraceSpeedCmPerSecond = 0.0f;
|
||||||
TArray<FVoxelTileKey> DesiredSorted; // critical floor prefix, then true-pawn distance-first
|
TArray<FVoxelTileKey> DesiredSorted; // critical floor prefix, then true-pawn distance-first
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# World Assembler M3 — Round 15 (S11)
|
||||||
|
|
||||||
|
Date: 2026-10-19
|
||||||
|
Starting revision: `b90d63d` (S10)
|
||||||
|
Result: shared spawn-phase apply budget enabled by default; geometry and recipe unchanged.
|
||||||
|
|
||||||
|
## Finding
|
||||||
|
|
||||||
|
The old 7.1 s result was an apply backlog layered on top of generation. In the matched authored-budget control, 2,578 initial tiles were generated. The four-visible-mesh cap was reached on 305 of 399 apply frames; the result backlog peaked at 252/256 and eight result-backlog-cap declines were recorded (decline rule 6). Dynamic near-reserve holds (rule 2) appeared on three frames in both runs; full task-cap holds (rule 1) appeared on 312 control frames and 309 default frames while the ten-task guard was occupied. Rule 6 (result backlog cap) appeared on two control frames, with eight declined tile attempts, and on zero default frames. Result-wait p95/max was 847.5/1,119.2 ms. LOD8's last worker finished at 6.068 s, but its result was not applied until 7.182 s.
|
||||||
|
|
||||||
|
I enabled the existing bounded startup apply mode in the shared `AVoxelWorld::ProcessPendingChunks` path: up to 12 visible applies or 14 ms per frame, capped at 30 seconds. It ends when the initial desired set and stationary walkable ring are ready, the desired epoch changes, a per-step override is active, or the duration expires. The regular authored budget (4 meshes/2 ms in this test) resumes for normal play. A trace record now reads back the CVar and resolved budgets. With the mode enabled, the peak backlog was 10/256, there were no backlog-cap declines, and result-wait p95/max fell to 16.6/19.3 ms. LOD8's last worker/apply were 6.015/6.019 s. The full set fell from 7.118 to 5.960 s, a 1.158 s (16.3%) improvement.
|
||||||
|
|
||||||
|
The remaining floor is generation/admission, not result application, collision cooking, or LOD4. The set's worker span was 5.581 s after the first request, with 26.786 worker-seconds total. Ten perfectly occupied slots would need 2.679 s, but the actor issued 2,578 requests over 310 submission passes (8.32 submissions/pass); sampled active-task guards averaged 9.90/10 while sampled running workers averaged 7.95 and peaked at 8. Across the request-to-ready interval, the trace integrates 4.78 busy-worker equivalents and 29.24 idle slot-seconds. The initial-set trace records one `scheduler_admission` on every actor frame from the first desired pass through ready, with no frame gaps. The control took 379 frames (6.758 s from first request), including 314 frames that submitted work and 65 drain-only frames; the default took 312 frames (5.602 s), 310 with submissions and two drain-only. That is about 56 admission frames/s, with 8.3 submissions per non-empty pass. The remaining floor is frame-paced admission/refill, with effective worker supply also below the ten task slots (running workers peaked at eight). The trace shows this occupancy limit but does not establish why the task backend tops out at eight.
|
||||||
|
|
||||||
|
Collision was not the startup floor: the 60 initial non-empty body updates had an 18.4 ms p95 submit-to-body-update interval, while the walkable ring was ready in 0.486 s. RMC section updates were not changed. LOD4 used the most aggregate worker time (6.590 s), but completed by 4.276 s; LOD8 remained the final LOD at 6.019 s. LOD4 averaged about 24.2 ms over the 272 tiles in the owner's count, but it did not set the tail, so I left its generation path alone.
|
||||||
|
|
||||||
|
## Matched spawn results
|
||||||
|
|
||||||
|
Fresh BuildHost processes, same binary, same 25 cm SurfaceWorld plan and streaming options; only the startup apply CVar differed. Both generated/drained 2,578 initial tiles and applied 1,333 visible meshes.
|
||||||
|
|
||||||
|
| Metric | Authored budget: CVar 0 | Startup budget: default 1 |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Full set ready | 7.118 s | 5.960 s |
|
||||||
|
| Walkable ring, 9/9 | 0.495 s | 0.486 s |
|
||||||
|
| Full LOD0 | 1.444 s | 1.403 s |
|
||||||
|
| Worker-seconds | 27.077 s | 26.786 s |
|
||||||
|
| Worker span from first request | 5.626 s | 5.581 s |
|
||||||
|
| First request to full ready | 6.758 s | 5.603 s |
|
||||||
|
| Ideal worker time, total / 10 slots | 2.708 s | 2.679 s |
|
||||||
|
| Integrated busy equivalents over worker span | 4.01/10 | 4.78/10 |
|
||||||
|
| Idle slot-seconds to full ready | 40.50 | 29.24 |
|
||||||
|
| Scheduler admission frames through ready | 379 | 312 |
|
||||||
|
| Non-empty submit passes / submissions per pass | 314 / 8.21 | 310 / 8.32 |
|
||||||
|
| Sampled active task guards, average / 10 | 8.24 | 9.90 |
|
||||||
|
| Sampled running workers, average / max | 6.62 / 8 | 7.95 / 8 |
|
||||||
|
| Worker queue p95 / max | 6.15 / 26.57 ms | 6.17 / 20.33 ms |
|
||||||
|
| Result queue p50 / p95 / max | 34.66 / 847.53 / 1,119.15 ms | 10.69 / 16.62 / 19.26 ms |
|
||||||
|
| Peak result backlog / cap | 252 / 256 | 10 / 256 |
|
||||||
|
| Backlog-cap declined tile attempts | 8 | 0 |
|
||||||
|
| Near-reserve decline frames | 3 | 3 |
|
||||||
|
| Apply frames through initial-ready | 399 | 332 |
|
||||||
|
| Apply-count cap frames | 305 | 0 |
|
||||||
|
| Max visible applies in one frame | 4 | 10 (12 allowed) |
|
||||||
|
| Total measured Apply time | 235.80 ms | 236.14 ms |
|
||||||
|
| Maximum Apply time in one frame | 1.576 ms | 2.357 ms |
|
||||||
|
| Initial non-empty body updates | 60 | 60 |
|
||||||
|
| Submit-to-body-update p95 / max | 17.69 / 17.81 ms | 18.37 / 18.45 ms |
|
||||||
|
|
||||||
|
The control spent 65 scheduler frames draining after its final non-empty submit pass; the default spent two. The startup CVar negative control is effective: `startup_apply_config` read back `0` for the control and `1` when omitted for the default run. Both records reported the authored budget as 4 meshes/2 ms and the startup budget as 12 meshes/14 ms. `startup_apply_budget` showed the default run entering and leaving the bounded phase. The cap, collision, reserve, order, speed and backlog settings were matched.
|
||||||
|
|
||||||
|
### Worker timeline
|
||||||
|
|
||||||
|
Busy worker slot-seconds are integrated from the first request, in elapsed one-second bins (the last bin is approximately 0.6 s). Each value is CVar 0 / default 1.
|
||||||
|
|
||||||
|
| Time from first request | Busy worker slot-seconds, CVar 0 / default 1 |
|
||||||
|
|---|---:|
|
||||||
|
| 0–1 s | 3.93 / 3.97 |
|
||||||
|
| 1–2 s | 3.92 / 3.92 |
|
||||||
|
| 2–3 s | 3.35 / 3.39 |
|
||||||
|
| 3–4 s | 6.63 / 6.48 |
|
||||||
|
| 4–5 s | 5.47 / 5.52 |
|
||||||
|
| 5–~5.6 s | 3.78 / 3.51 |
|
||||||
|
|
||||||
|
The apply change does not materially change generation occupancy. It removes the long post-generation queue wait, especially for the far LODs. Last visible apply per LOD, measured from the same spawn origin:
|
||||||
|
|
||||||
|
| LOD | Worker-seconds CVar 0 / default 1 | Last apply CVar 0 / default 1 |
|
||||||
|
|---|---:|---:|
|
||||||
|
| 0 | 3.827 / 3.725 | 1.444 / 1.403 s |
|
||||||
|
| 1 | 3.792 / 3.895 | 2.292 / 2.283 s |
|
||||||
|
| 2 | 0.813 / 0.806 | 2.857 / 2.849 s |
|
||||||
|
| 3 | 2.139 / 2.064 | 3.355 / 3.330 s |
|
||||||
|
| 4 | 6.685 / 6.590 | 4.352 / 4.276 s |
|
||||||
|
| 5 | 1.599 / 1.528 | 4.930 / 4.660 s |
|
||||||
|
| 6 | 2.076 / 2.077 | 5.383 / 5.060 s |
|
||||||
|
| 7 | 1.865 / 1.928 | 5.802 / 5.338 s |
|
||||||
|
| 8 | 4.280 / 4.171 | 7.182 / 6.019 s |
|
||||||
|
|
||||||
|
LOD8 last worker/apply were 6.068/7.182 s in the control and 6.015/6.019 s with the startup budget. That 1.114 s control-side wait nearly vanishes under the higher budget. LOD4 remains the highest total worker cost but not the completion tail.
|
||||||
|
|
||||||
|
### Tick and 40 m/s ring
|
||||||
|
|
||||||
|
`UWorld::Tick` p95/max from the matched 6 m/s spawn and subsequent normal-walk windows:
|
||||||
|
|
||||||
|
| Window | CVar 0 spawn | Default 1 spawn | CVar 0 walk | Default 1 walk |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| Tick p95 / max | 1.805 / 5.374 ms | 2.740 / 5.175 ms | 2.364 / 28.993 ms | 2.483 / 28.876 ms |
|
||||||
|
|
||||||
|
The normal-walk p95 changed by 0.119 ms and max by -0.117 ms; the normal authored budget is restored after startup. In a separate fresh 40 m/s default run, 75/75 moving support-center samples had the full 9/9 ring (100%); first/last sample were 30.207/45.015 s. That high-speed stress run measured spawn tick p95/max 2.426/14.061 ms and walk tick p95/max 31.428/56.628 ms. It is a 40 m/s stress measure, not the normal-walk comparison.
|
||||||
|
|
||||||
|
## Quality and determinism
|
||||||
|
|
||||||
|
No density, mesh generation, collision arithmetic or recipe output changed, so no recipe version bump is needed. The apply pacing change is shared by all world families. All requested gates passed:
|
||||||
|
|
||||||
|
- Core off at 12.5 cm: legacy path passed.
|
||||||
|
- M2 Hybrid V1 and V2 fixtures: all analytic fixtures and border checks passed.
|
||||||
|
- SurfaceWorld BuildHost at 25 cm: passed; actor collision, anchors and carve/fill checks passed; water remains fixed for the season.
|
||||||
|
- FloatingIslands BuildHost at 25 cm: passed; capsule contact, island/span height, carve/fill and switch-off legacy checks passed.
|
||||||
|
- S3/S4 standalone audit: S3 72 samples, max 0.0427 cm; S4 128 samples, max 0.0504 cm; both within 2 cm. The 100 cm negative control was detected for both. Cap census 385/385, coverage 2,520/2,520 (100%), no misses.
|
||||||
|
- 40 m/s walkable ring: 75/75 samples at 9/9 (100%).
|
||||||
|
|
||||||
|
Current-build worker hash matrices were stable:
|
||||||
|
|
||||||
|
| Run family / workers | Density | Mesh | Payload | Topology |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Standalone W1/W8/W12 | `c10697a8f48380c7` | `e75c9119a1498cd5` | `49a9a8d1835a6c8c` | `edcb23288b5e8724` |
|
||||||
|
| SurfaceWorld BuildHost W1/W8/W16 | `0cd2a1a4152ee4aa` | `a06243aee16b65d5` | `c700162935c262d7` | `0af901aa42b452bc` |
|
||||||
|
| FloatingIslands BuildHost W1/W8/W16 | `4dcfb37585d7d986` | `f14f76fbf065c6ed` | `53035c45fd1a082e` | `23550100654a4ec3` |
|
||||||
|
|
||||||
|
## Commands and artifacts
|
||||||
|
|
||||||
|
Fresh matched 6 m/s pair (the first command is the CVar negative control; the second omits the override to exercise the code default):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -TimeoutSeconds 900
|
||||||
|
```
|
||||||
|
|
||||||
|
40 m/s ring confirmation:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -TimeoutSeconds 900
|
||||||
|
```
|
||||||
|
|
||||||
|
Quality commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreOffTest -CoreVoxelSizeCm 12.5 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV2 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -FloatingIslandsOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||||
|
.\Standalone\bin\VoxelForgeCoreStandalone.exe --world-assembler-audit --workers=12 --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||||
|
```
|
||||||
|
|
||||||
|
Determinism commands were run serially at Normal priority and affinity 4095. Standalone used the same command for workers 1, 8 and 12:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=$workers --world-assembler-prototype --stream-benchmark --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||||
|
```
|
||||||
|
|
||||||
|
BuildHost W1/W8/W16 matrices were part of the SurfaceWorld and FloatingIslands 25 cm gates above. Build commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\BuildVoxelForgeCoreHost.ps1 -MaxParallelActions 12 -TimeoutSeconds 900
|
||||||
|
.\Standalone\Build.ps1 -Target All -CompileWorkers 12 -BuildPriority Normal
|
||||||
|
```
|
||||||
|
|
||||||
|
The BuildHost trace JSONL files remain under `Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/`; paired names begin `CoreWorldValidation-owner_surface_stream_trace_only_applyboost_0_...move_600...` and `...applyboost_default_...move_600...`. The 40 m/s trace uses `...applyboost_default_...move_4000...`. They remain in Saved, including files over 300 KB. Standalone stdout logs are in `Saved/BuildHost/Round15/`.
|
||||||
|
|
||||||
|
Final binary SHA-256:
|
||||||
|
|
||||||
|
| Binary | SHA-256 |
|
||||||
|
|---|---|
|
||||||
|
| BuildHost `UnrealEditor-VoxelForge.dll` | `E2744E97F3690A4B125E01E4AC89CD21D6771BFD70F72B71301411FDDAEFA56C` |
|
||||||
|
| BuildHost `UnrealEditor-VoxelForgeCore.dll` | `D87CC56BACCDE6559705EEAEA7DB73D1A6ABF79484D37A3887B94A9FD62BABD3` |
|
||||||
|
| BuildHost `UnrealEditor-VoxelForgeEditor.dll` | `C7F00CD4BD150236C1BAD2BB2D54E6617A9F4313C1CFE64487928CD400442459` |
|
||||||
|
| Standalone `VoxelForgeCoreStandalone.exe` | `68C81625911DF9764354C3D75A059B8A735423110D66EF3D50916199F066E9ED` |
|
||||||
|
|
||||||
|
## Build, run and async-safety notes
|
||||||
|
|
||||||
|
- BuildHost compiled successfully with 12 max parallel actions. Standalone `All` compiled successfully with 12 workers at Normal priority. The existing `vswhere.exe` lookup warnings and `VoxelStrateManager.cpp` C4838 conversion warnings remain.
|
||||||
|
- Fresh processes were run serially, at Normal priority, with a 12-thread maximum. Free memory was above 17 GiB before the final launches; measured BuildHost peak private memory stayed below 3.7 GiB. No Unreal/Standalone process is running now. Each step stayed under the 15-minute limit.
|
||||||
|
- A first Standalone determinism launcher invocation had a PowerShell parse error from `W$workers:` interpolation before launching any process. The launcher was corrected to `${workers}` and all W1/W8/W12 runs passed. No binary run failed, no `0xC0000142` occurred and no retry was needed.
|
||||||
|
- `Saved/BuildHost/HostProject/Saved/Crashes/` contains only the pre-existing `UECC-Windows-F1A3EFD5469F5DD62E68EBBACAC84376_0000` (2026-09-17); no new UECC folder appeared.
|
||||||
|
- Async safety was checked in the source diff: no worker or queue behavior changed. Epoch validation, MPSC queue modes, EndPlay drain, worker `bShuttingDown` checks, `ModsLock`, region-page lifetime, and game-thread UObject/RMC mutation are untouched. The only new actor field records whether the first trace config record has been emitted.
|
||||||
|
- No new source file. Compile spots: `Source/VoxelForge/Private/VoxelWorld.cpp` and `Source/VoxelForge/Public/VoxelWorld.h` compile in the BuildHost VoxelForge module; the harness edit is PowerShell only. No generated header, recipe core, or `.uasset` changed. Owner `.uproject` and editor were not opened or built. RealtimeMeshComponent was read-only.
|
||||||
|
- No commit, push or stash.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `Source/VoxelForge/Private/VoxelWorld.cpp` — default-on bounded startup budget and trace readback of the effective CVar/budgets.
|
||||||
|
- `Source/VoxelForge/Public/VoxelWorld.h` — per-world one-time trace guard, with French and English comment.
|
||||||
|
- `Tools/TestVoxelForgeCoreWorldHost.ps1` — `-1` means no override (exercise code default); explicit `0` remains the negative control.
|
||||||
|
- `CODEMAP.md` section 3 — shared drain and trace behavior.
|
||||||
|
- `ARCHITECTURE.md` section 8.32 — behavior and measured floor.
|
||||||
|
|
||||||
|
### Dialogue
|
||||||
|
|
||||||
|
**User:** Count first, identify the new 7.1 s floor from the trace, move shared behavior only when the CVar control proves it, and retain every quality/determinism gate.
|
||||||
|
|
||||||
|
**Codex:** The 4-mesh cap was holding completed LOD8 results for up to 1.1 s. The bounded 12-mesh/14 ms startup budget removed that backlog and brought the set to 5.96 s while preserving the 0.486 s walkable ring and all quality gates. The remaining measured span is 5.58 s of generation/admission; the trace narrows it to frame-paced refill and worker supply but cannot isolate actor tick refill from task-backend dispatch variation.
|
||||||
@@ -6,7 +6,7 @@ param(
|
|||||||
[ValidateSet('Auto', 'Legacy', 'Dynamic')][string]$NearReserveMode = 'Auto',
|
[ValidateSet('Auto', 'Legacy', 'Dynamic')][string]$NearReserveMode = 'Auto',
|
||||||
[switch]$EnforceCollisionGate,
|
[switch]$EnforceCollisionGate,
|
||||||
[switch]$StartupTrace,
|
[switch]$StartupTrace,
|
||||||
[ValidateRange(0, 1)][int]$StartupApplyBoost = 0,
|
[ValidateRange(-1, 1)][int]$StartupApplyBoost = -1,
|
||||||
[switch]$StreamingTraceOnly,
|
[switch]$StreamingTraceOnly,
|
||||||
[ValidateRange(1, 6000)][int]$TestMoveSpeedCmPerSecond = 600,
|
[ValidateRange(1, 6000)][int]$TestMoveSpeedCmPerSecond = 600,
|
||||||
[ValidateRange(0, 1)][int]$StreamingCollisionLookahead = 1,
|
[ValidateRange(0, 1)][int]$StreamingCollisionLookahead = 1,
|
||||||
@@ -62,7 +62,8 @@ $RunName = if ($FloatingIslandsOnly) { "floating_islands$CoreSizeSuffix" }
|
|||||||
elseif ($CorePlanOnly) { if ($CoreTestSeed -ne [int]::MinValue) { "plan_seed_$CoreTestSeed$CoreSizeSuffix" } else { "plan_only$CoreSizeSuffix" } }
|
elseif ($CorePlanOnly) { if ($CoreTestSeed -ne [int]::MinValue) { "plan_seed_$CoreTestSeed$CoreSizeSuffix" } else { "plan_only$CoreSizeSuffix" } }
|
||||||
else { "owner_surface$CoreSizeSuffix" }
|
else { "owner_surface$CoreSizeSuffix" }
|
||||||
$RunName += $(if ($StreamingTraceOnly) { '_stream_trace_only' } else { '' })
|
$RunName += $(if ($StreamingTraceOnly) { '_stream_trace_only' } else { '' })
|
||||||
$RunName += "_applyboost_$StartupApplyBoost"
|
$StartupApplyBoostLabel = if ($StartupApplyBoost -lt 0) { 'default' } else { [string]$StartupApplyBoost }
|
||||||
|
$RunName += "_applyboost_$StartupApplyBoostLabel"
|
||||||
|
|
||||||
if ($MismatchedManifestFallback) { $CoreFallbackTest = $true }
|
if ($MismatchedManifestFallback) { $CoreFallbackTest = $true }
|
||||||
$SelectedModeFlags = @(
|
$SelectedModeFlags = @(
|
||||||
@@ -314,7 +315,9 @@ $LegacyPendingResultCapValue = if ($NearReserveMode -eq 'Legacy') { 1 } else { 0
|
|||||||
$BaseArguments += "-voxel.StreamingCoarseFirst=$CoarseFirstValue"
|
$BaseArguments += "-voxel.StreamingCoarseFirst=$CoarseFirstValue"
|
||||||
$BaseArguments += "-voxel.StreamingDynamicNearReserve=$DynamicReserveValue"
|
$BaseArguments += "-voxel.StreamingDynamicNearReserve=$DynamicReserveValue"
|
||||||
$BaseArguments += "-voxel.StreamingLegacyPendingResultCap=$LegacyPendingResultCapValue"
|
$BaseArguments += "-voxel.StreamingLegacyPendingResultCap=$LegacyPendingResultCapValue"
|
||||||
$BaseArguments += "-voxel.StreamingStartupApplyBoost=$StartupApplyBoost"
|
if ($StartupApplyBoost -ge 0) {
|
||||||
|
$BaseArguments += "-voxel.StreamingStartupApplyBoost=$StartupApplyBoost"
|
||||||
|
}
|
||||||
$BaseArguments += "-voxel.CoreRegionConcurrentAdmission=$CoreRegionConcurrentAdmission"
|
$BaseArguments += "-voxel.CoreRegionConcurrentAdmission=$CoreRegionConcurrentAdmission"
|
||||||
$BaseArguments += "-voxel.StreamingResultBacklogCap=$StreamingResultBacklogCap"
|
$BaseArguments += "-voxel.StreamingResultBacklogCap=$StreamingResultBacklogCap"
|
||||||
if ($Step1MaxMeshApplies -gt 0) {
|
if ($Step1MaxMeshApplies -gt 0) {
|
||||||
@@ -474,7 +477,7 @@ if ($StreamingTraceOnly) {
|
|||||||
if ($NewCrashes.Count -gt 0) {
|
if ($NewCrashes.Count -gt 0) {
|
||||||
throw "New UECC crash folders appeared during the stream trace: $($NewCrashes -join '; ')"
|
throw "New UECC crash folders appeared during the stream trace: $($NewCrashes -join '; ')"
|
||||||
}
|
}
|
||||||
Write-Host "VOXELFORGE_CORE_STREAM_TRACE_PASS boost=$StartupApplyBoost order=$StreamingOrder"
|
Write-Host "VOXELFORGE_CORE_STREAM_TRACE_PASS boost=$StartupApplyBoostLabel order=$StreamingOrder"
|
||||||
Write-Host "Core actor log: $FinalLog"
|
Write-Host "Core actor log: $FinalLog"
|
||||||
Write-Host "Startup trace: $TracePath"
|
Write-Host "Startup trace: $TracePath"
|
||||||
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
||||||
|
|||||||
Reference in new issue
Block a user