wip(assembler): round S9 -- collision look-ahead: ground ring ready on arrival up to 40 m/s and 50 m/s falls
voxel.StreamingCollisionLookahead (default on) predicts the support tile 350 ms ahead along full 3D velocity and prioritises its 9-tile collision ring. Owner plan, BuildHost actor: 40 m/s arrival-ready 25/74 -> 74/74 (CVar-off control 15/74); 6/12/25 m/s stay 100%; -Z 50 m/s 93/93. Spawn unchanged (~7.7 s). Critical LOD0 path at 40 m/s: generation 67 ms p50, RMC body update 38 ms p50. LOD8 cost counted (native horizon 48 ms of 60), optimisation deferred. 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
8440adc742
commit
e7586a1256
7 files changed
+507
-70
No files matched your search
@@ -1609,6 +1609,45 @@ The cap kept the observed backlog finite without reinstating the S6/S7 finished-
|
|||||||
decline. Full timings, per-center records, quality checks and hashes are in
|
decline. Full timings, per-center records, quality checks and hashes are in
|
||||||
[`Standalone/Research/WorldAssemblerM3/Round12/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round12/REPORT.md).
|
[`Standalone/Research/WorldAssemblerM3/Round12/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round12/REPORT.md).
|
||||||
|
|
||||||
|
### 8.30 Velocity-scaled support-ring lookahead — World Assembler S9 / M3 Round 13
|
||||||
|
|
||||||
|
The ring gate uses the support tile, which can sit one tile below the clipmap center on a surface
|
||||||
|
world. Both scheduler branches must use the same `GetPlayerSupportTile` result for the active 3x3
|
||||||
|
collision-critical ring and its predicted rings. Predicting from the stream center can spend work on
|
||||||
|
the wrong vertical slice, so the trace includes both `center` and `walkable_center`.
|
||||||
|
|
||||||
|
`voxel.StreamingCollisionLookahead` defaults to `1`; `0` disables only the prediction and is the
|
||||||
|
negative control. `GetStreamingHeadingStep` transforms velocity into actor space and selects its
|
||||||
|
dominant axis, including Z for a fall; when motion is zero it falls back to pawn forward. The
|
||||||
|
lookahead targets 350 ms of travel, rounds up to whole active LOD0 tiles, and clamps the lead to one
|
||||||
|
or two rings. The scheduler adds the future 3x3 keys only when they already belong to the desired
|
||||||
|
clipmap, then moves those collision keys into `CriticalDesiredTiles`. It does not reserve more worker
|
||||||
|
slots, expand the clipmap, cancel in-flight jobs, or change tile arithmetic. `collision_lookahead`
|
||||||
|
records speed, direction, stream/support centers, promoted-key count and missing desired keys.
|
||||||
|
|
||||||
|
On the staged SurfaceWorld owner plan at 25 cm, the S9 `+Y` trace found the ring ready at all 74/74
|
||||||
|
support-center arrivals through 40 m/s; the corrected-source CVar-off control found 15/74. The
|
||||||
|
S8 no-lookahead baseline was 25/74. At 50 m/s scripted downward motion, 93/93 support-center
|
||||||
|
arrivals were ready. The 40 m/s matched on/off initial-ready times were 7.743/7.778 s, so the
|
||||||
|
prediction did not slow spawn in this pair. These figures support 40 m/s as the highest horizontal
|
||||||
|
speed tested; they do not measure a higher horizontal ceiling. The vertical trace is a scripted
|
||||||
|
actor move through the owner plan, not a free-fall physics replay.
|
||||||
|
|
||||||
|
For 108 critical LOD0 tile jobs with complete stage joins at 40 m/s, request-to-worker start was
|
||||||
|
0.02/9.59 ms p50/p95, generation 66.6/84.0 ms, result wait 9.3/17.7 ms, game-thread apply to mesh
|
||||||
|
submit 0.17/0.24 ms, RMC collision submit 0.01/0.02 ms, and RMC submit-to-body-update 37.6/85.9 ms.
|
||||||
|
The latter is the observable body-ready interval, not a Chaos-only CPU timer. The current trace saw
|
||||||
|
no scheduler skips, a 113-result peak under the 256 cap and 33 ms maximum region-page compile with
|
||||||
|
concurrent admission. Generation and body update dominate the measured tile path; the lookahead
|
||||||
|
gives those stages time to finish before the support center changes. LOD0-missing clipmap frames
|
||||||
|
remain separately visible in the Round 13 table; they are not equivalent to a missing support ring.
|
||||||
|
|
||||||
|
The scheduler change does not alter field, mesh or collision output, so `WorldAssemblerPlanV1` and
|
||||||
|
the far-horizon recipe versions stay unchanged. Core-off legacy, M2 fixtures, SurfaceWorld and
|
||||||
|
FloatingIslands checks passed; the S3/S4 ground, cap and coverage audit passed. The full sweep,
|
||||||
|
negative control, hashes, stage ledger and the deferred LOD8 optimization plan are in
|
||||||
|
[`Standalone/Research/WorldAssemblerM3/Round13/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round13/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
|
||||||
|
|||||||
+12
@@ -1041,6 +1041,18 @@ separate from the region-gate control. Round12 found 11/11 ready support centers
|
|||||||
immediately ready at 60 m/s with a 400 m teleport; see the traces under `Saved/BuildHost` and
|
immediately ready at 60 m/s with a 400 m teleport; see the traces under `Saved/BuildHost` and
|
||||||
[`Standalone/Research/WorldAssemblerM3/Round12/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round12/REPORT.md).
|
[`Standalone/Research/WorldAssemblerM3/Round12/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round12/REPORT.md).
|
||||||
|
|
||||||
|
### 3.16 Velocity-scaled support-ring lookahead — World Assembler S9 / M3 Round 13
|
||||||
|
|
||||||
|
`voxel.StreamingCollisionLookahead` defaults on; `0` is the negative control. The shared
|
||||||
|
`GetStreamingHeadingStep` uses actor-local motion, including vertical speed. The support tile from
|
||||||
|
`GetPlayerSupportTile` is the reference for both the active 3x3 critical ring and up to two future
|
||||||
|
3x3 rings. `GetStreamingCollisionLookaheadTiles` aims 350 ms ahead, rounded up to whole LOD0 tiles
|
||||||
|
and capped at two. Prediction only promotes keys already inside the desired clipmap. The
|
||||||
|
`collision_lookahead` trace records stream/support centers, direction, speed, ring count and any
|
||||||
|
missing desired keys. `VoxelForgeExploreCommandlet.cpp` and `Tools/TestVoxelForgeCoreWorldHost.ps1`
|
||||||
|
accept a movement vector so the same trace covers horizontal and vertical travel. See ARCHITECTURE
|
||||||
|
§8.30 and [`Standalone/Research/WorldAssemblerM3/Round13/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round13/REPORT.md).
|
||||||
|
|
||||||
## 4. The density pipeline (most-edited hot path)
|
## 4. The density pipeline (most-edited hot path)
|
||||||
|
|
||||||
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
|
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
|
||||||
|
|||||||
@@ -197,6 +197,12 @@ namespace
|
|||||||
GVoxelForgeStreamingDynamicNearReserve,
|
GVoxelForgeStreamingDynamicNearReserve,
|
||||||
TEXT("Keep the authored near-worker reserve while moving or before the walkable ring is ready; default 1 releases idle slots after that gate, 0 restores the legacy fixed reserve."));
|
TEXT("Keep the authored near-worker reserve while moving or before the walkable ring is ready; default 1 releases idle slots after that gate, 0 restores the legacy fixed reserve."));
|
||||||
|
|
||||||
|
int32 GVoxelForgeStreamingCollisionLookahead = 1;
|
||||||
|
FAutoConsoleVariableRef CVarVoxelForgeStreamingCollisionLookahead(
|
||||||
|
TEXT("voxel.StreamingCollisionLookahead"),
|
||||||
|
GVoxelForgeStreamingCollisionLookahead,
|
||||||
|
TEXT("Request up to two collision rings ahead along player velocity; 0 disables predictive ring admission."));
|
||||||
|
|
||||||
int32 GVoxelForgeStreamingLegacyPendingResultCap = 0;
|
int32 GVoxelForgeStreamingLegacyPendingResultCap = 0;
|
||||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingLegacyPendingResultCap(
|
FAutoConsoleVariableRef CVarVoxelForgeStreamingLegacyPendingResultCap(
|
||||||
TEXT("voxel.StreamingLegacyPendingResultCap"),
|
TEXT("voxel.StreamingLegacyPendingResultCap"),
|
||||||
@@ -2056,6 +2062,7 @@ void AVoxelWorld::BeginPlay()
|
|||||||
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
|
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
|
||||||
int32 CommandLineCollisionGate = GVoxelForgeCollisionGate;
|
int32 CommandLineCollisionGate = GVoxelForgeCollisionGate;
|
||||||
int32 CommandLineCoreRegionConcurrentAdmission = GVoxelForgeCoreRegionConcurrentAdmission;
|
int32 CommandLineCoreRegionConcurrentAdmission = GVoxelForgeCoreRegionConcurrentAdmission;
|
||||||
|
int32 CommandLineStreamingCollisionLookahead = GVoxelForgeStreamingCollisionLookahead;
|
||||||
int32 CommandLineStreamingResultBacklogCap = GVoxelForgeStreamingResultBacklogCap;
|
int32 CommandLineStreamingResultBacklogCap = GVoxelForgeStreamingResultBacklogCap;
|
||||||
int32 CommandLineStep1GenerationCap = GVoxelForgeStep1GenerationCap;
|
int32 CommandLineStep1GenerationCap = GVoxelForgeStep1GenerationCap;
|
||||||
int32 CommandLineStep1ExitAfterFrames = GVoxelForgeStep1ExitAfterFrames;
|
int32 CommandLineStep1ExitAfterFrames = GVoxelForgeStep1ExitAfterFrames;
|
||||||
@@ -2134,6 +2141,12 @@ void AVoxelWorld::BeginPlay()
|
|||||||
GVoxelForgeStreamingDynamicNearReserve);
|
GVoxelForgeStreamingDynamicNearReserve);
|
||||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingLegacyPendingResultCap="),
|
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingLegacyPendingResultCap="),
|
||||||
GVoxelForgeStreamingLegacyPendingResultCap);
|
GVoxelForgeStreamingLegacyPendingResultCap);
|
||||||
|
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingCollisionLookahead="),
|
||||||
|
CommandLineStreamingCollisionLookahead))
|
||||||
|
{
|
||||||
|
GVoxelForgeStreamingCollisionLookahead =
|
||||||
|
CommandLineStreamingCollisionLookahead != 0 ? 1 : 0;
|
||||||
|
}
|
||||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreRegionConcurrentAdmission="),
|
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreRegionConcurrentAdmission="),
|
||||||
CommandLineCoreRegionConcurrentAdmission))
|
CommandLineCoreRegionConcurrentAdmission))
|
||||||
{
|
{
|
||||||
@@ -6896,8 +6909,8 @@ void AVoxelWorld::Tick(float DeltaTime)
|
|||||||
AdvanceHeadlessCollisionGateStressTest(PlayerLastPos, PlayerHeading, PlayerPawn);
|
AdvanceHeadlessCollisionGateStressTest(PlayerLastPos, PlayerHeading, PlayerPawn);
|
||||||
AdvanceHeadlessDigMotionTest(PlayerLastPos, PlayerPawn);
|
AdvanceHeadlessDigMotionTest(PlayerLastPos, PlayerPawn);
|
||||||
PeakObservedPawnSpeedCmPerSecond = FMath::Max(
|
PeakObservedPawnSpeedCmPerSecond = FMath::Max(
|
||||||
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size2D());
|
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size());
|
||||||
StartupTraceSpeedCmPerSecond = PlayerHeading.Size2D();
|
StartupTraceSpeedCmPerSecond = PlayerHeading.Size();
|
||||||
UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime);
|
UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime);
|
||||||
RecordHeadlessDigMotionFrame(PlayerPawn, PlayerLastPos, DeltaTime);
|
RecordHeadlessDigMotionFrame(PlayerPawn, PlayerLastPos, DeltaTime);
|
||||||
ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn);
|
ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn);
|
||||||
@@ -10253,6 +10266,52 @@ void AVoxelWorld::BuildDesiredTransitionIndex()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FIntVector AVoxelWorld::GetStreamingHeadingStep(const FVector& PlayerHeading,
|
||||||
|
const APawn* PlayerPawn) const
|
||||||
|
{
|
||||||
|
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
|
||||||
|
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
|
||||||
|
{
|
||||||
|
Heading = GetActorTransform().InverseTransformVectorNoScale(
|
||||||
|
PlayerPawn->GetActorForwardVector());
|
||||||
|
}
|
||||||
|
|
||||||
|
const float AbsX = FMath::Abs(Heading.X);
|
||||||
|
const float AbsY = FMath::Abs(Heading.Y);
|
||||||
|
const float AbsZ = FMath::Abs(Heading.Z);
|
||||||
|
if (AbsX <= KINDA_SMALL_NUMBER && AbsY <= KINDA_SMALL_NUMBER
|
||||||
|
&& AbsZ <= KINDA_SMALL_NUMBER)
|
||||||
|
{
|
||||||
|
return FIntVector::ZeroValue;
|
||||||
|
}
|
||||||
|
if (AbsX >= AbsY && AbsX >= AbsZ)
|
||||||
|
{
|
||||||
|
return FIntVector(Heading.X >= 0.0f ? 1 : -1, 0, 0);
|
||||||
|
}
|
||||||
|
if (AbsY >= AbsZ)
|
||||||
|
{
|
||||||
|
return FIntVector(0, Heading.Y >= 0.0f ? 1 : -1, 0);
|
||||||
|
}
|
||||||
|
return FIntVector(0, 0, Heading.Z >= 0.0f ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 AVoxelWorld::GetStreamingCollisionLookaheadTiles(
|
||||||
|
const FVector& PlayerHeading, const FIntVector& HeadingStep) const
|
||||||
|
{
|
||||||
|
if (GVoxelForgeStreamingCollisionLookahead == 0
|
||||||
|
|| HeadingStep == FIntVector::ZeroValue || PlayerHeading.Size() < 50.0f)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aim 350 ms ahead, round up to a whole level-0 tile, and cap the lead at two tiles.
|
||||||
|
// Viser 350 ms d'avance, arrondir au tile L0 superieur et limiter l'avance a deux tiles.
|
||||||
|
const float TileWidthCm = static_cast<float>(CHUNK_SIZE) * GetActiveVoxelSizeCm();
|
||||||
|
const int32 RequestedTiles = FMath::CeilToInt(
|
||||||
|
PlayerHeading.Size() * 0.35f / FMath::Max(TileWidthCm, 1.0f));
|
||||||
|
return FMath::Clamp(RequestedTiles, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
|
void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
|
||||||
APawn* PlayerPawn, const FVector& PlayerHeading,
|
APawn* PlayerPawn, const FVector& PlayerHeading,
|
||||||
TArray<FVoxelTileKey>& OutLeavers)
|
TArray<FVoxelTileKey>& OutLeavers)
|
||||||
@@ -10279,6 +10338,12 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
|||||||
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
|
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FIntVector LookaheadHeadingStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
|
||||||
|
const int32 LookaheadTiles = GetStreamingCollisionLookaheadTiles(
|
||||||
|
PlayerHeading, LookaheadHeadingStep);
|
||||||
|
int32 LookaheadCollisionTilesAdded = 0;
|
||||||
|
int32 LookaheadDesiredTilesMissing = 0;
|
||||||
|
|
||||||
TArray<FVoxelTileKey> CandidateKeys;
|
TArray<FVoxelTileKey> CandidateKeys;
|
||||||
TSet<FVoxelTileKey> CandidateSet;
|
TSet<FVoxelTileKey> CandidateSet;
|
||||||
TSet<FVoxelTileKey> BaseVisualTiles;
|
TSet<FVoxelTileKey> BaseVisualTiles;
|
||||||
@@ -10357,6 +10422,49 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
|||||||
CollisionAnchorTiles.Add(Key);
|
CollisionAnchorTiles.Add(Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FVoxelTileKey PlayerTile(GetTileCoordAtLocalCm(WorldToLocalCm(PlayerPosition)), 0);
|
||||||
|
FVoxelTileKey OccupiedTile = PlayerTile;
|
||||||
|
FVoxelTileKey SupportTile;
|
||||||
|
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
|
||||||
|
{
|
||||||
|
OccupiedTile = SupportTile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve the future 3x3 collision rings inside the existing LOD0 clipmap.
|
||||||
|
// Reserver les prochains anneaux 3x3 dans le clipmap LOD0 deja present.
|
||||||
|
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
|
||||||
|
{
|
||||||
|
const FIntVector AheadCenter = OccupiedTile.Coord + LookaheadHeadingStep * Ahead;
|
||||||
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
|
{
|
||||||
|
const FVoxelTileKey Key(AheadCenter + FIntVector(Dx, Dy, 0), 0);
|
||||||
|
if (!CandidateSet.Contains(Key))
|
||||||
|
{
|
||||||
|
++LookaheadDesiredTilesMissing;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!CollisionDesiredTiles.Contains(Key))
|
||||||
|
{
|
||||||
|
CollisionDesiredTiles.Add(Key);
|
||||||
|
++LookaheadCollisionTilesAdded;
|
||||||
|
}
|
||||||
|
if (!BaseVisualTiles.Contains(Key)) CollisionAnchorTiles.Add(Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (LookaheadTiles > 0 && VoxelForgeStartupTrace::IsActive())
|
||||||
|
{
|
||||||
|
VoxelForgeStartupTrace::RecordEvent(TEXT("collision_lookahead"), FString::Printf(
|
||||||
|
TEXT("\"center\":[%d,%d,%d],\"walkable_center\":[%d,%d,%d],"
|
||||||
|
"\"heading_step\":[%d,%d,%d],\"speed_cm_s\":%s,"
|
||||||
|
"\"lookahead_tiles\":%d,\"collision_tiles_added\":%d,\"desired_tiles_missing\":%d"),
|
||||||
|
Center.X, Center.Y, Center.Z, OccupiedTile.Coord.X, OccupiedTile.Coord.Y,
|
||||||
|
OccupiedTile.Coord.Z,
|
||||||
|
LookaheadHeadingStep.X, LookaheadHeadingStep.Y, LookaheadHeadingStep.Z,
|
||||||
|
*FString::SanitizeFloat(PlayerHeading.Size()), LookaheadTiles,
|
||||||
|
LookaheadCollisionTilesAdded, LookaheadDesiredTilesMissing));
|
||||||
|
}
|
||||||
|
|
||||||
for (const FVoxelTileKey& Key : CollisionAnchorTiles)
|
for (const FVoxelTileKey& Key : CollisionAnchorTiles)
|
||||||
{
|
{
|
||||||
if (!BaseVisualTiles.Contains(Key) && !FullVisualAnchorTiles.Contains(Key))
|
if (!BaseVisualTiles.Contains(Key) && !FullVisualAnchorTiles.Contains(Key))
|
||||||
@@ -10391,13 +10499,6 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
|||||||
}
|
}
|
||||||
CancelObsoleteTileWork();
|
CancelObsoleteTileWork();
|
||||||
|
|
||||||
const FVoxelTileKey PlayerTile(GetTileCoordAtLocalCm(WorldToLocalCm(PlayerPosition)), 0);
|
|
||||||
FVoxelTileKey OccupiedTile = PlayerTile;
|
|
||||||
FVoxelTileKey SupportTile;
|
|
||||||
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
|
|
||||||
{
|
|
||||||
OccupiedTile = SupportTile;
|
|
||||||
}
|
|
||||||
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
|
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
|
||||||
{
|
{
|
||||||
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
|
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
|
||||||
@@ -10415,22 +10516,7 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
|||||||
}
|
}
|
||||||
AddCriticalTile(OccupiedTile);
|
AddCriticalTile(OccupiedTile);
|
||||||
|
|
||||||
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
|
const FIntVector HeadingStep = LookaheadHeadingStep;
|
||||||
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
|
|
||||||
{
|
|
||||||
Heading = GetActorTransform().InverseTransformVectorNoScale(
|
|
||||||
PlayerPawn->GetActorForwardVector());
|
|
||||||
}
|
|
||||||
FIntVector HeadingStep = FIntVector::ZeroValue;
|
|
||||||
const float AbsX = FMath::Abs(Heading.X);
|
|
||||||
const float AbsY = FMath::Abs(Heading.Y);
|
|
||||||
const float AbsZ = FMath::Abs(Heading.Z);
|
|
||||||
if (AbsX > KINDA_SMALL_NUMBER || AbsY > KINDA_SMALL_NUMBER || AbsZ > KINDA_SMALL_NUMBER)
|
|
||||||
{
|
|
||||||
if (AbsX >= AbsY && AbsX >= AbsZ) HeadingStep.X = Heading.X >= 0.0f ? 1 : -1;
|
|
||||||
else if (AbsY >= AbsZ) HeadingStep.Y = Heading.Y >= 0.0f ? 1 : -1;
|
|
||||||
else HeadingStep.Z = Heading.Z >= 0.0f ? 1 : -1;
|
|
||||||
}
|
|
||||||
if (HeadingStep != FIntVector::ZeroValue)
|
if (HeadingStep != FIntVector::ZeroValue)
|
||||||
{
|
{
|
||||||
FVoxelTileKey NextTile = OccupiedTile;
|
FVoxelTileKey NextTile = OccupiedTile;
|
||||||
@@ -10443,9 +10529,19 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
|||||||
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
{
|
{
|
||||||
const FVoxelTileKey WalkableKey(PlayerTile.Coord + FIntVector(Dx, Dy, 0), 0);
|
const FVoxelTileKey WalkableKey(OccupiedTile.Coord + FIntVector(Dx, Dy, 0), 0);
|
||||||
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
||||||
}
|
}
|
||||||
|
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
|
||||||
|
{
|
||||||
|
const FIntVector AheadCenter = OccupiedTile.Coord + HeadingStep * Ahead;
|
||||||
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
|
{
|
||||||
|
const FVoxelTileKey WalkableKey(AheadCenter + FIntVector(Dx, Dy, 0), 0);
|
||||||
|
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int32 Index = CriticalDesiredTiles.Num() - 1; Index >= 0; --Index)
|
for (int32 Index = CriticalDesiredTiles.Num() - 1; Index >= 0; --Index)
|
||||||
{
|
{
|
||||||
const int32 ExistingIndex = DesiredSorted.Find(CriticalDesiredTiles[Index]);
|
const int32 ExistingIndex = DesiredSorted.Find(CriticalDesiredTiles[Index]);
|
||||||
@@ -10710,6 +10806,11 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
|||||||
++DesiredEpoch;
|
++DesiredEpoch;
|
||||||
DesiredEpochStartCycles = FPlatformTime::Cycles64();
|
DesiredEpochStartCycles = FPlatformTime::Cycles64();
|
||||||
DesiredEpochCenterChunk = Center;
|
DesiredEpochCenterChunk = Center;
|
||||||
|
const FIntVector LookaheadHeadingStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
|
||||||
|
const int32 LookaheadTiles = GetStreamingCollisionLookaheadTiles(
|
||||||
|
PlayerHeading, LookaheadHeadingStep);
|
||||||
|
int32 LookaheadCollisionTilesAdded = 0;
|
||||||
|
int32 LookaheadDesiredTilesMissing = 0;
|
||||||
|
|
||||||
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 6;
|
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 6;
|
||||||
const int32 MaxLevel = Settings ? FMath::Clamp(Settings->MaxClipLevel, 0, 8) : 4;
|
const int32 MaxLevel = Settings ? FMath::Clamp(Settings->MaxClipLevel, 0, 8) : 4;
|
||||||
@@ -10791,6 +10892,49 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
|||||||
CollisionDesiredTiles.Add(Key);
|
CollisionDesiredTiles.Add(Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FVoxelTileKey PlayerTile(
|
||||||
|
WorldToChunkCoord(WorldToLocalCm(PlayerPosition)), 0);
|
||||||
|
FVoxelTileKey OccupiedTile = PlayerTile;
|
||||||
|
FVoxelTileKey SupportTile;
|
||||||
|
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
|
||||||
|
{
|
||||||
|
OccupiedTile = SupportTile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve predictive collision inside the current clipmap so those tiles cook before arrival.
|
||||||
|
// Reserver la collision predictive dans le clipmap courant pour cuire avant l'arrivee.
|
||||||
|
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
|
||||||
|
{
|
||||||
|
const FIntVector AheadCenter = OccupiedTile.Coord + LookaheadHeadingStep * Ahead;
|
||||||
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
|
{
|
||||||
|
const FVoxelTileKey Key(AheadCenter + FIntVector(Dx, Dy, 0), 0);
|
||||||
|
if (!IsDesired(Key))
|
||||||
|
{
|
||||||
|
++LookaheadDesiredTilesMissing;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!CollisionDesiredTiles.Contains(Key))
|
||||||
|
{
|
||||||
|
CollisionDesiredTiles.Add(Key);
|
||||||
|
++LookaheadCollisionTilesAdded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (LookaheadTiles > 0 && VoxelForgeStartupTrace::IsActive())
|
||||||
|
{
|
||||||
|
VoxelForgeStartupTrace::RecordEvent(TEXT("collision_lookahead"), FString::Printf(
|
||||||
|
TEXT("\"center\":[%d,%d,%d],\"walkable_center\":[%d,%d,%d],"
|
||||||
|
"\"heading_step\":[%d,%d,%d],\"speed_cm_s\":%s,"
|
||||||
|
"\"lookahead_tiles\":%d,\"collision_tiles_added\":%d,\"desired_tiles_missing\":%d"),
|
||||||
|
Center.X, Center.Y, Center.Z, OccupiedTile.Coord.X, OccupiedTile.Coord.Y,
|
||||||
|
OccupiedTile.Coord.Z,
|
||||||
|
LookaheadHeadingStep.X, LookaheadHeadingStep.Y, LookaheadHeadingStep.Z,
|
||||||
|
*FString::SanitizeFloat(PlayerHeading.Size()), LookaheadTiles,
|
||||||
|
LookaheadCollisionTilesAdded, LookaheadDesiredTilesMissing));
|
||||||
|
}
|
||||||
|
|
||||||
if (GVoxelForgeProfileTileGeneration != 0)
|
if (GVoxelForgeProfileTileGeneration != 0)
|
||||||
{
|
{
|
||||||
int32 RenderLOD0Count = 0;
|
int32 RenderLOD0Count = 0;
|
||||||
@@ -10860,15 +11004,6 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
|||||||
// one that can catch a falling pawn — followed by the adjacent tile in the dominant movement
|
// one that can catch a falling pawn — followed by the adjacent tile in the dominant movement
|
||||||
// direction. Keep the actor-centre tile as a fallback for capsule/offset edge cases. Only keys
|
// direction. Keep the actor-centre tile as a fallback for capsule/offset edge cases. Only keys
|
||||||
// in the current desired set are promoted, so this never expands the streaming footprint.
|
// in the current desired set are promoted, so this never expands the streaming footprint.
|
||||||
const FVoxelTileKey PlayerTile(
|
|
||||||
WorldToChunkCoord(WorldToLocalCm(PlayerPosition)), 0);
|
|
||||||
FVoxelTileKey OccupiedTile = PlayerTile;
|
|
||||||
FVoxelTileKey SupportTile;
|
|
||||||
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
|
|
||||||
{
|
|
||||||
OccupiedTile = SupportTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
|
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
|
||||||
{
|
{
|
||||||
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
|
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
|
||||||
@@ -10889,31 +11024,7 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
|||||||
}
|
}
|
||||||
AddCriticalTile(OccupiedTile);
|
AddCriticalTile(OccupiedTile);
|
||||||
|
|
||||||
FVector Heading = PlayerHeading;
|
const FIntVector HeadingStep = LookaheadHeadingStep;
|
||||||
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
|
|
||||||
{
|
|
||||||
Heading = PlayerPawn->GetActorForwardVector();
|
|
||||||
}
|
|
||||||
Heading = GetActorTransform().InverseTransformVectorNoScale(Heading);
|
|
||||||
const float AbsX = FMath::Abs(Heading.X);
|
|
||||||
const float AbsY = FMath::Abs(Heading.Y);
|
|
||||||
const float AbsZ = FMath::Abs(Heading.Z);
|
|
||||||
FIntVector HeadingStep = FIntVector::ZeroValue;
|
|
||||||
if (AbsX > KINDA_SMALL_NUMBER || AbsY > KINDA_SMALL_NUMBER || AbsZ > KINDA_SMALL_NUMBER)
|
|
||||||
{
|
|
||||||
if (AbsX >= AbsY && AbsX >= AbsZ)
|
|
||||||
{
|
|
||||||
HeadingStep.X = Heading.X >= 0.0f ? 1 : -1;
|
|
||||||
}
|
|
||||||
else if (AbsY >= AbsZ)
|
|
||||||
{
|
|
||||||
HeadingStep.Y = Heading.Y >= 0.0f ? 1 : -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
HeadingStep.Z = Heading.Z >= 0.0f ? 1 : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (HeadingStep != FIntVector::ZeroValue)
|
if (HeadingStep != FIntVector::ZeroValue)
|
||||||
{
|
{
|
||||||
FVoxelTileKey NextTile = OccupiedTile;
|
FVoxelTileKey NextTile = OccupiedTile;
|
||||||
@@ -10926,9 +11037,19 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
|||||||
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
{
|
{
|
||||||
const FVoxelTileKey WalkableKey(PlayerTile.Coord + FIntVector(Dx, Dy, 0), 0);
|
const FVoxelTileKey WalkableKey(OccupiedTile.Coord + FIntVector(Dx, Dy, 0), 0);
|
||||||
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
||||||
}
|
}
|
||||||
|
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
|
||||||
|
{
|
||||||
|
const FIntVector AheadCenter = OccupiedTile.Coord + HeadingStep * Ahead;
|
||||||
|
for (int32 Dy = -1; Dy <= 1; ++Dy)
|
||||||
|
for (int32 Dx = -1; Dx <= 1; ++Dx)
|
||||||
|
{
|
||||||
|
const FVoxelTileKey WalkableKey(AheadCenter + FIntVector(Dx, Dy, 0), 0);
|
||||||
|
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert in reverse so CriticalDesiredTiles[0] remains the occupied floor, [1] the heading
|
// Insert in reverse so CriticalDesiredTiles[0] remains the occupied floor, [1] the heading
|
||||||
// tile, and all ordinary distance-sorted work follows both.
|
// tile, and all ordinary distance-sorted work follows both.
|
||||||
@@ -11378,7 +11499,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
|||||||
? LastStreamingSchedulerSampleSeconds : StreamingUpdateStartSeconds;
|
? LastStreamingSchedulerSampleSeconds : StreamingUpdateStartSeconds;
|
||||||
const double IntervalSeconds = FMath::Max(0.0, Now - IntervalStart);
|
const double IntervalSeconds = FMath::Max(0.0, Now - IntervalStart);
|
||||||
const double BusySeconds = FPlatformTime::ToSeconds64(WorkerCyclesDelta);
|
const double BusySeconds = FPlatformTime::ToSeconds64(WorkerCyclesDelta);
|
||||||
const double SpeedCmPerSecond = PlayerHeading.Size2D();
|
const double SpeedCmPerSecond = PlayerHeading.Size();
|
||||||
VoxelForgeStartupTrace::RecordEvent(TEXT("scheduler_skip"), FString::Printf(
|
VoxelForgeStartupTrace::RecordEvent(TEXT("scheduler_skip"), FString::Printf(
|
||||||
TEXT("\"frame\":%llu,\"reason\":\"%s\",\"desired_count\":%d,"
|
TEXT("\"frame\":%llu,\"reason\":\"%s\",\"desired_count\":%d,"
|
||||||
"\"desired_missing\":%d,\"desired_missing_by_lod\":%s,"
|
"\"desired_missing\":%d,\"desired_missing_by_lod\":%s,"
|
||||||
@@ -11589,7 +11710,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
|||||||
bGateCenterChanged ? WalkableCenter.Y - PreviousGateCenter.Y : 0,
|
bGateCenterChanged ? WalkableCenter.Y - PreviousGateCenter.Y : 0,
|
||||||
bGateCenterChanged ? WalkableCenter.Z - PreviousGateCenter.Z : 0,
|
bGateCenterChanged ? WalkableCenter.Z - PreviousGateCenter.Z : 0,
|
||||||
bGateCenterChanged ? 1 : 0, bWalkableRingReadyForReserve ? 1 : 0,
|
bGateCenterChanged ? 1 : 0, bWalkableRingReadyForReserve ? 1 : 0,
|
||||||
WalkableRingReadyCount, *FString::SanitizeFloat(PlayerHeading.Size2D()), DesiredEpoch));
|
WalkableRingReadyCount, *FString::SanitizeFloat(PlayerHeading.Size()), DesiredEpoch));
|
||||||
bHasLastWalkableGateSample = true;
|
bHasLastWalkableGateSample = true;
|
||||||
LastWalkableGateCenter = WalkableCenter;
|
LastWalkableGateCenter = WalkableCenter;
|
||||||
bLastWalkableGateReady = bWalkableRingReadyForReserve;
|
bLastWalkableGateReady = bWalkableRingReadyForReserve;
|
||||||
@@ -11600,7 +11721,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
|||||||
RecordSchedulerSkip(TEXT("core_region_compile_pending"));
|
RecordSchedulerSkip(TEXT("core_region_compile_pending"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool bPawnMoving = PlayerHeading.Size2D() >= 50.0f;
|
const bool bPawnMoving = PlayerHeading.Size() >= 50.0f;
|
||||||
EffectiveNearTaskReserve = GVoxelForgeStreamingDynamicNearReserve == 0
|
EffectiveNearTaskReserve = GVoxelForgeStreamingDynamicNearReserve == 0
|
||||||
? AuthoredNearTaskReserve
|
? AuthoredNearTaskReserve
|
||||||
: ((bPawnMoving || !bWalkableRingReadyForReserve) ? AuthoredNearTaskReserve : 0);
|
: ((bPawnMoving || !bWalkableRingReadyForReserve) ? AuthoredNearTaskReserve : 0);
|
||||||
@@ -12219,7 +12340,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
|||||||
CenterChunk.X, CenterChunk.Y, CenterChunk.Z,
|
CenterChunk.X, CenterChunk.Y, CenterChunk.Z,
|
||||||
*FString::SanitizeFloat(LocalPawn.X), *FString::SanitizeFloat(LocalPawn.Y),
|
*FString::SanitizeFloat(LocalPawn.X), *FString::SanitizeFloat(LocalPawn.Y),
|
||||||
*FString::SanitizeFloat(LocalPawn.Z),
|
*FString::SanitizeFloat(LocalPawn.Z),
|
||||||
*FString::SanitizeFloat(PlayerHeading.Size2D()),
|
*FString::SanitizeFloat(PlayerHeading.Size()),
|
||||||
bWalkableRingReadyForReserve ? 1 : 0,
|
bWalkableRingReadyForReserve ? 1 : 0,
|
||||||
*DesiredCounts, *ResidentCounts, *PendingCounts,
|
*DesiredCounts, *ResidentCounts, *PendingCounts,
|
||||||
GetActiveGenerationTaskCount(), GetGenerationAdmissionCount(), MaxTasks, NearTaskReserve,
|
GetActiveGenerationTaskCount(), GetGenerationAdmissionCount(), MaxTasks, NearTaskReserve,
|
||||||
|
|||||||
@@ -702,6 +702,10 @@ private:
|
|||||||
void BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
|
void BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
|
||||||
APawn* PlayerPawn, const FVector& PlayerHeading,
|
APawn* PlayerPawn, const FVector& PlayerHeading,
|
||||||
TArray<FVoxelTileKey>& OutLeavers);
|
TArray<FVoxelTileKey>& OutLeavers);
|
||||||
|
FIntVector GetStreamingHeadingStep(const FVector& PlayerHeading,
|
||||||
|
const APawn* PlayerPawn) const;
|
||||||
|
int32 GetStreamingCollisionLookaheadTiles(const FVector& PlayerHeading,
|
||||||
|
const FIntVector& HeadingStep) const;
|
||||||
void StartPendingCoreRegionCompile();
|
void StartPendingCoreRegionCompile();
|
||||||
void LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority WorkPriority);
|
void LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority WorkPriority);
|
||||||
void EnqueueStreamingResult(FChunkResult&& Result);
|
void EnqueueStreamingResult(FChunkResult&& Result);
|
||||||
|
|||||||
@@ -17490,6 +17490,24 @@ bool RunCoreWorldActorIntegrationTest(const FExploreArguments& Arguments, FStrin
|
|||||||
float WalkSpeedCmPerSecond = 600.0f;
|
float WalkSpeedCmPerSecond = 600.0f;
|
||||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestMoveSpeedCmPerSecond="),
|
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestMoveSpeedCmPerSecond="),
|
||||||
WalkSpeedCmPerSecond);
|
WalkSpeedCmPerSecond);
|
||||||
|
FString WalkDirectionText(TEXT("0,1,0"));
|
||||||
|
const FString DirectionKey(TEXT("voxel.TestMoveDirection="));
|
||||||
|
const FString CommandLineText(FCommandLine::Get());
|
||||||
|
const int32 DirectionKeyIndex = CommandLineText.Find(
|
||||||
|
DirectionKey, ESearchCase::IgnoreCase, ESearchDir::FromStart);
|
||||||
|
if (DirectionKeyIndex != INDEX_NONE)
|
||||||
|
{
|
||||||
|
WalkDirectionText = CommandLineText.Mid(DirectionKeyIndex + DirectionKey.Len());
|
||||||
|
const int32 DirectionEnd = WalkDirectionText.Find(
|
||||||
|
TEXT(" "), ESearchCase::CaseSensitive, ESearchDir::FromStart);
|
||||||
|
if (DirectionEnd != INDEX_NONE)
|
||||||
|
{
|
||||||
|
WalkDirectionText.LeftInline(DirectionEnd, EAllowShrinking::No);
|
||||||
|
}
|
||||||
|
WalkDirectionText.TrimStartAndEndInline();
|
||||||
|
WalkDirectionText.RemoveFromStart(TEXT("\""));
|
||||||
|
WalkDirectionText.RemoveFromEnd(TEXT("\""));
|
||||||
|
}
|
||||||
TArray<double> SpawnTickMilliseconds;
|
TArray<double> SpawnTickMilliseconds;
|
||||||
TArray<double> WalkTickMilliseconds;
|
TArray<double> WalkTickMilliseconds;
|
||||||
TArray<TPair<double, double>> TickSamples;
|
TArray<TPair<double, double>> TickSamples;
|
||||||
@@ -17569,11 +17587,11 @@ bool RunCoreWorldActorIntegrationTest(const FExploreArguments& Arguments, FStrin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UE_LOG(LogTemp, Display,
|
UE_LOG(LogTemp, Display,
|
||||||
TEXT("[VoxelForgeCoreWorldStreamTrace] pass complete=1 recipe=%s voxel_cm=%.3f walk_speed_cm_s=%.0f walk_direction=+Y"),
|
TEXT("[VoxelForgeCoreWorldStreamTrace] pass complete=1 recipe=%s voxel_cm=%.3f walk_speed_cm_s=%.0f walk_direction=%s"),
|
||||||
Arguments.CoreHybridMesherVersion == 2 ? TEXT("WorldAssemblerHybridV2")
|
Arguments.CoreHybridMesherVersion == 2 ? TEXT("WorldAssemblerHybridV2")
|
||||||
: (Arguments.CoreHybridMesherVersion == 1 ? TEXT("WorldAssemblerHybridV1")
|
: (Arguments.CoreHybridMesherVersion == 1 ? TEXT("WorldAssemblerHybridV1")
|
||||||
: TEXT("MarchingCubesV1")),
|
: TEXT("MarchingCubesV1")),
|
||||||
Arguments.CoreVoxelSizeCm, WalkSpeedCmPerSecond);
|
Arguments.CoreVoxelSizeCm, WalkSpeedCmPerSecond, *WalkDirectionText);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
# World assembler S9 / M3 Round 13 — support-ring lookahead
|
||||||
|
|
||||||
|
**Result:** the support ring is ready at all tested center arrivals through 40 m/s horizontally,
|
||||||
|
and through a scripted 50 m/s vertical descent. At 40 m/s the matched CVar-off control reached
|
||||||
|
the ring at only 15/74 arrivals; lookahead reached it at 74/74. Initial readiness was 7.743 s with
|
||||||
|
lookahead and 7.778 s without it. I did not reserve more workers, and I left the LOD8 geometry
|
||||||
|
unchanged; its counted optimization plan is in §5.
|
||||||
|
|
||||||
|
The lookahead is default-on through `voxel.StreamingCollisionLookahead=1`; `0` disables prediction.
|
||||||
|
It follows the support tile, predicts 350 ms ahead (rounded up to one or two 8 m LOD0 tiles at the
|
||||||
|
25 cm owner scale), and promotes only rings already inside the desired clipmap. I first predicted
|
||||||
|
from the stream center. The trace showed that this was one tile above the walkable center on this
|
||||||
|
SurfaceWorld plan. I corrected both scheduler branches to predict and prioritize from
|
||||||
|
`GetPlayerSupportTile`. No field or mesh arithmetic changed, so the recipe versions remain as-is.
|
||||||
|
|
||||||
|
## 1. Speed sweep
|
||||||
|
|
||||||
|
Fresh BuildHost commandlets used the staged owner SurfaceWorld plan, seed 0, 25 cm Core scale,
|
||||||
|
Legacy streaming order, Dynamic near reserve, apply boost 0, region concurrent admission 1, result
|
||||||
|
backlog cap 256, Normal process priority, a 12-action host limit, and movement from 30 to 45 s.
|
||||||
|
The S8 traces are the pre-lookahead baseline. Arrival readiness is the `walkable_gate.ready` value
|
||||||
|
on the support-center-change event. Lag is the maximum time to a later 9/9 sample before the next
|
||||||
|
support-center change; unresolved centers are counted separately. LOD0 missing is the number of
|
||||||
|
`scheduler_admission` samples with any desired LOD0 key missing during 30–45 s.
|
||||||
|
|
||||||
|
| Motion | S8 arrival-ready | S8 worst resolved lag / unresolved | S8 LOD0-missing frames | S9 arrival-ready | S9 worst lag / unresolved | S9 LOD0-missing frames |
|
||||||
|
|---|---:|---:|---:|---:|---:|---:|
|
||||||
|
| +Y 6 m/s | 11/11 (100%) | 0 ms / 0 | 168/838 (20.0%) | 11/11 (100%) | 0 ms / 0 | 165/839 (19.7%) |
|
||||||
|
| +Y 12 m/s | 22/22 (100%) | 0 ms / 0 | 431/776 (55.5%) | 22/22 (100%) | 0 ms / 0 | 436/775 (56.3%) |
|
||||||
|
| +Y 25 m/s | 46/46 (100%) | 0 ms / 0 | 625/677 (92.3%) | 46/46 (100%) | 0 ms / 0 | 627/679 (92.3%) |
|
||||||
|
| +Y 40 m/s | 25/74 (33.8%) | 191.1 ms / 8 | 579/595 (97.3%) | 74/74 (100%) | 0 ms / 0 | 576/593 (97.1%) |
|
||||||
|
| −Z 50 m/s | — | — | — | 93/93 (100%) | 0 ms / 0 | 605/626 (96.6%) |
|
||||||
|
|
||||||
|
The matched 40 m/s CVar-negative control on the corrected source reached 15/74 centers (20.3%),
|
||||||
|
with 196.3 ms worst resolved lag, 7 unresolved centers, and 574/590 LOD0-missing samples (97.3%).
|
||||||
|
This isolates prediction from the support-center correction. The old S8 60 m/s + 400 m teleport
|
||||||
|
remains a separate discontinuity case: 8/113 centers were ready at arrival, 3 more became ready
|
||||||
|
before the next center, and 102/113 never reached 9/9.
|
||||||
|
|
||||||
|
At 40 m/s the current run therefore supports **40 m/s as the highest tested horizontal speed**;
|
||||||
|
it does not establish a higher horizontal ceiling. The 50 m/s trace used the harness's scripted
|
||||||
|
downward actor movement through the staged owner plan, not a Chaos free-fall simulation in a shaft.
|
||||||
|
The ring remained ready through all 93 center changes.
|
||||||
|
|
||||||
|
Spawn did not regress in the matched 40 m/s pair: `initial_ready_s` was 7.743349 s on and 7.777605 s
|
||||||
|
off, with spawn-tick p95 2.288 ms and 2.292 ms respectively. The S9 on-run initial-ready values
|
||||||
|
at 6/12/25/40 m/s were 7.813133/7.815265/7.821660/7.743349 s. Movement begins at 30 s, and the
|
||||||
|
lookahead adds no stationary-spawn requests.
|
||||||
|
|
||||||
|
## 2. Critical path at 40 m/s
|
||||||
|
|
||||||
|
For 108 traced critical LOD0 jobs with complete tile-event joins, p50/p95 were:
|
||||||
|
|
||||||
|
| Stage | p50 | p95 |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Request → worker start | 0.02 ms | 9.59 ms |
|
||||||
|
| Worker generation | 66.6 ms | 84.0 ms |
|
||||||
|
| Result queue → game-thread apply | 9.3 ms | 17.7 ms |
|
||||||
|
| Game-thread apply → mesh submit | 0.17 ms | 0.24 ms |
|
||||||
|
| Mesh → collision submit | 0.01 ms | 0.02 ms |
|
||||||
|
| RMC submit → body-update event | 37.6 ms | 85.9 ms |
|
||||||
|
|
||||||
|
The body-update interval is the observable collision-ready delay; it is not an isolated Chaos CPU
|
||||||
|
cook timer. Generation and body update dominate. Request-to-worker delay is small at the median,
|
||||||
|
and game-thread apply/submission is below a quarter millisecond at p95. The lookahead gives the
|
||||||
|
generation and body-update stages time to finish before support-center arrival.
|
||||||
|
|
||||||
|
At 40 m/s the trace reported two lookahead tiles and three newly collision-promoted keys per
|
||||||
|
scheduler rebuild, with zero desired keys outside the clipmap. Worker occupancy hit the 10-task
|
||||||
|
limit, while the result queue peaked at 113 entries / 16.8 MB under the 256 cap and recorded no cap
|
||||||
|
stops. Region compile time was 1.74 ms p50, 22.16 ms p95, 33.32 ms max; concurrent admission
|
||||||
|
recorded no global scheduler skips. Those systems still consume time, but the matched CVar test
|
||||||
|
shows prediction is sufficient at this plan's 40 m/s horizontal limit without adding reserved
|
||||||
|
slots.
|
||||||
|
|
||||||
|
## 3. Output quality and determinism
|
||||||
|
|
||||||
|
- BuildHost Core-off legacy passed at 12.5 cm. M2 HybridV1 and HybridV2 each passed five analytic
|
||||||
|
fixtures plus border checks at 25 cm.
|
||||||
|
- SurfaceWorld passed at 25 cm with collision, anchor movement, carve and travel checks. Its
|
||||||
|
W1/W8/W16 actor hash matrix was stable: density `0cd2a1a4152ee4aa`, mesh `80a5b054b904b1a2`,
|
||||||
|
payload `bf2f1b40dff38acc`, topology `56074198682d5e18`. Peak commandlet private memory was
|
||||||
|
3.90 GB.
|
||||||
|
- FloatingIslands passed at 25 cm with fixed water, edits, collision and LOD0/1 coverage; the
|
||||||
|
BuildHost actor matrix reported W1/W8/W16. Peak private memory was 3.25 GB.
|
||||||
|
- The current Standalone W1/W8/W12 full-clipmap and per-LOD 0–8 density, mesh, payload and topology
|
||||||
|
hashes match. Full-clipmap hash tuple: density `c10697a8f48380c7`, mesh `430d2be2bdd8b988`,
|
||||||
|
payload `b7b33e14092225c9`, topology `c5c46d66606a819e`. Full-detail hashes also match.
|
||||||
|
- The Standalone S4 audit passed: 385/385 far-cap bands, 128 agreement points (maximum 0.0504 cm
|
||||||
|
under the 2 cm gate), the 100 cm offset negative control, and 2,520/2,520 cap-coverage rays.
|
||||||
|
The current BuildHost SurfaceWorld S3 check passed 128 points with 0.0427 cm maximum far/near
|
||||||
|
error and detected its 100 cm offset control.
|
||||||
|
- A sampled Standalone W12 benchmark process peaked at 0.68 GB private memory.
|
||||||
|
|
||||||
|
I did **not** run Standalone `--workers=16`: that implementation constructs 16 worker threads,
|
||||||
|
which conflicts with the explicit 12-thread launch cap. Current Standalone W1/W8/W12 match, and
|
||||||
|
the BuildHost SurfaceWorld and FloatingIslands W1/W8/W16 matrices passed. I requested whether the
|
||||||
|
owner wants a one-run W16 exception; until then the 12-thread cap stands. No new `UECC-*` folder
|
||||||
|
was present; the only folder was the existing 2026-09-17 crash.
|
||||||
|
|
||||||
|
No geometry, field, density or recipe output changed. The far-horizon recipe stays at v2 and the
|
||||||
|
SurfaceWorld plan identity stays unchanged. Water remains fixed for the season; player carve/fill
|
||||||
|
remains unrestricted.
|
||||||
|
|
||||||
|
## 4. Goal 2 — counted LOD8 plan, optimization deferred
|
||||||
|
|
||||||
|
The S8 cold owner trace measured 306 LOD8 jobs at 18.798 worker-seconds total (61.4 ms average,
|
||||||
|
79.8 ms p95, 94.7 ms max), 45.6% of worker time. Its full set reached readiness in about 7.7 s.
|
||||||
|
The current 25 cm BuildHost feature-cost probe warmed eight LOD8 keys and reported p95 per-tile
|
||||||
|
diagnostics: full/candidate feature path 59.508/59.585 ms, column computation 30.771 ms, marching
|
||||||
|
cubes 6.018 ms, native ground+cap horizon refinement 48.205 ms, classification 0.0046 ms, feature
|
||||||
|
list 0.0007 ms, and shell work 0.0001 ms (repair skipped). The profile counters are overlapping
|
||||||
|
inclusive measurements, so they must not be summed. The same probe reported 15,529 field calls,
|
||||||
|
9,679 column-cache misses, 8,192 refined ground triangles, 531 ground triangles removed, and
|
||||||
|
6,732 cap triangles at the sampled high-cost LOD8 tile.
|
||||||
|
|
||||||
|
`HeightfieldColumnCache` is a 65,536-entry thread-local cache keyed by global XY. It reuses columns
|
||||||
|
within a worker, but does not share cache entries across workers. The current probe does not count
|
||||||
|
cross-tile duplicate samples by worker, and does not split ground-refinement time from cap-refinement
|
||||||
|
time. The next measurement should add exclusive counters for column evaluation, MC, ground refine,
|
||||||
|
cap refine, classification, and cache hits/misses per tile and worker, then trace duplicate global
|
||||||
|
columns across neighboring LOD8 jobs.
|
||||||
|
|
||||||
|
No LOD8 optimization or far-mesh change was made this round, so no before/after far-horizon/cap
|
||||||
|
captures were produced and the below-7.7 s Goal 2 target remains open. The counted next step is to
|
||||||
|
prototype shared immutable heightfield-column pages for planar families, compare against sampling
|
||||||
|
at displayed resolution and tolerance-based refinement skips, then rerun S3 ground/S4 cap gates
|
||||||
|
(2 cm agreement, 100% coverage) and matched viewer captures at VSync/60 fps, closing the viewer
|
||||||
|
right after each capture. Do not relax tolerances without a metre-scale error bound.
|
||||||
|
|
||||||
|
## 5. Builds, commands, binaries and run record
|
||||||
|
|
||||||
|
BuildHost and Standalone builds passed on their first attempt. Standalone build printed the existing
|
||||||
|
`vswhere.exe` lookup warning and C4838 conversions in `VoxelStrateManager.cpp:3974–3975`, then
|
||||||
|
built both targets successfully. All commandlets were fresh, serial, Normal priority and below
|
||||||
|
8 GB; the largest sampled BuildHost private memory was 3.90 GB. No process exceeded the 15-minute
|
||||||
|
step limit.
|
||||||
|
|
||||||
|
Build commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\BuildVoxelForgeCoreHost.ps1 -MaxParallelActions 12 -TimeoutSeconds 900
|
||||||
|
.\Standalone\Build.ps1 -Target All -CompileWorkers 12 -BuildPriority Normal
|
||||||
|
```
|
||||||
|
|
||||||
|
The final horizontal trace command (repeat with speeds `600`, `1200`, `2500`, and `4000` cm/s):
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
The pre-S9 baseline used the same staged plan and settings with the S8 binary, before the lookahead
|
||||||
|
CVar existed; repeat with the same four speed values:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -TimeoutSeconds 900
|
||||||
|
```
|
||||||
|
|
||||||
|
Vertical descent and the matched 40 m/s CVar-negative control:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,0,-1' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 5000 -TimeoutSeconds 900
|
||||||
|
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 0 -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
|
||||||
|
```
|
||||||
|
|
||||||
|
Standalone determinism command template (fresh process, `--workers=1`, `8`, and `12`; `16` was
|
||||||
|
not launched under the thread cap):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=1 --world-assembler-prototype --stream-benchmark --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||||
|
```
|
||||||
|
|
||||||
|
One initial CVar-off command was rejected by PowerShell before launch because the movement vector
|
||||||
|
was not quoted (`Cannot convert value to type System.String`). I retried with
|
||||||
|
`-TestMoveDirection '0,1,0'`; the BuildHost run passed. No Unreal process launched for the rejected
|
||||||
|
attempt. No other test/build attempt failed or retried.
|
||||||
|
|
||||||
|
Binary SHA-256:
|
||||||
|
|
||||||
|
| Binary | SHA-256 |
|
||||||
|
|---|---|
|
||||||
|
| UE 5.7 `UnrealEditor-Cmd.exe` | `ECCE9996191F7F7E69B3D104EECDD0E678AE69176FDCCD79828C3E75A0654D6D` |
|
||||||
|
| S8 baseline BuildHost `UnrealEditor-VoxelForge.dll` | `8B82EE6C6FBB511AE766EB3D8646A7E9491952F41445CB62D49D71D0B9783418` |
|
||||||
|
| S8 baseline BuildHost `UnrealEditor-VoxelForgeEditor.dll` | `B3BC5E61172C5B21D928A5ACB5F22F8D9E00179B481B675804838DE9A620010D` |
|
||||||
|
| BuildHost `UnrealEditor-VoxelForge.dll` | `F542FC1F014190798485CC088470FD498D368D2E369639EAD32A0EEE8FDB1193` |
|
||||||
|
| BuildHost `UnrealEditor-VoxelForgeCore.dll` | `0A1EB7BB27C3C74A4AA0996E0A24E74224D7B8FFFFF113B1071FA42E18E8375A` |
|
||||||
|
| BuildHost `UnrealEditor-VoxelForgeEditor.dll` | `15B6460FD4E48D14CBE751C99447F06B62B44CF35FFD440AED106585AECEDC79` |
|
||||||
|
| `Standalone/bin/VoxelForgeCoreStandalone.exe` | `FD4CD39CD49BB0E8429250295BD16FACA0A6FBE412240522AB79C086245978C0` |
|
||||||
|
|
||||||
|
Trace JSONL and actor stdout remain under
|
||||||
|
`Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/`. The initial
|
||||||
|
lookahead results are retained in `Saved/BuildHost/Round13/BeforeSupportTileCorrection/`; Standalone
|
||||||
|
hash logs and the S4 audit log are under `Saved/BuildHost/Round13/`. No file over 300 KB was added
|
||||||
|
outside `Saved/BuildHost/`. No `.uproject`, editor, `.uasset`, RealtimeMeshComponent source, commit,
|
||||||
|
push or stash was touched.
|
||||||
|
|
||||||
|
Modified source files:
|
||||||
|
|
||||||
|
- `Source/VoxelForge/Private/VoxelWorld.cpp` — CVar, support-centered lookahead and critical ring ordering; BuildHost `VoxelForge` runtime compile.
|
||||||
|
- `Source/VoxelForge/Public/VoxelWorld.h` — private helper declarations; same runtime compile.
|
||||||
|
- `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` — movement-vector harness; BuildHost `VoxelForgeEditor` compile.
|
||||||
|
- `Tools/TestVoxelForgeCoreWorldHost.ps1` — movement-vector and CVar arguments.
|
||||||
|
|
||||||
|
New source files: none. Standalone source files were not changed. Documentation updated:
|
||||||
|
`CODEMAP.md` §3.16 and `ARCHITECTURE.md` §8.30.
|
||||||
|
|
||||||
|
Async-safety diff review: `Epoch` propagation/checks are untouched; all result queues remain
|
||||||
|
`EQueueMode::Mpsc`; `EndPlay` still drains after active tasks finish; workers still check
|
||||||
|
`bShuttingDown`; `ModsLock` and immutable region-page lifetime are unchanged; UObject and RMC
|
||||||
|
mutation remain on the game thread.
|
||||||
|
|
||||||
|
### Dialogue
|
||||||
|
|
||||||
|
**User:** Continue S9 from the 6 and 12 m/s lookahead traces after the service interruption.
|
||||||
|
**Luna:** Resumed at the 25 m/s run, found and fixed the support/stream-center mismatch, then measured the corrected speed sweep and matched 40 m/s CVar control.
|
||||||
@@ -9,6 +9,8 @@ param(
|
|||||||
[ValidateRange(0, 1)][int]$StartupApplyBoost = 0,
|
[ValidateRange(0, 1)][int]$StartupApplyBoost = 0,
|
||||||
[switch]$StreamingTraceOnly,
|
[switch]$StreamingTraceOnly,
|
||||||
[ValidateRange(1, 6000)][int]$TestMoveSpeedCmPerSecond = 600,
|
[ValidateRange(1, 6000)][int]$TestMoveSpeedCmPerSecond = 600,
|
||||||
|
[ValidateRange(0, 1)][int]$StreamingCollisionLookahead = 1,
|
||||||
|
[string]$TestMoveDirection = '0,1,0',
|
||||||
[ValidateRange(0, 1)][int]$CoreRegionConcurrentAdmission = 1,
|
[ValidateRange(0, 1)][int]$CoreRegionConcurrentAdmission = 1,
|
||||||
[ValidateRange(0, 4096)][int]$StreamingResultBacklogCap = 256,
|
[ValidateRange(0, 4096)][int]$StreamingResultBacklogCap = 256,
|
||||||
[ValidateRange(0, 900)][int]$Step1MaxMeshApplies = 0,
|
[ValidateRange(0, 900)][int]$Step1MaxMeshApplies = 0,
|
||||||
@@ -79,7 +81,18 @@ if ($StreamingTraceOnly -and ($SharedFeatureAuditOnly -or $EnableHybridFixturePl
|
|||||||
$HybridCliffOnly -or $HybridFixturesOnly -or $WorldAssemblyManifestPath)) {
|
$HybridCliffOnly -or $HybridFixturesOnly -or $WorldAssemblyManifestPath)) {
|
||||||
throw '-StreamingTraceOnly uses the staged owner plan without additional fixture or manifest overrides.'
|
throw '-StreamingTraceOnly uses the staged owner plan without additional fixture or manifest overrides.'
|
||||||
}
|
}
|
||||||
|
if ($TestMoveDirection -notmatch '^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+),[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+),[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$') {
|
||||||
|
throw '-TestMoveDirection must be three comma-separated invariant-culture numbers without spaces.'
|
||||||
|
}
|
||||||
|
$MoveDirectionComponents = @($TestMoveDirection.Split(',') | ForEach-Object {
|
||||||
|
[double]::Parse($_, [Globalization.CultureInfo]::InvariantCulture)
|
||||||
|
})
|
||||||
|
if ($MoveDirectionComponents[0] -eq 0 -and $MoveDirectionComponents[1] -eq 0 -and
|
||||||
|
$MoveDirectionComponents[2] -eq 0) {
|
||||||
|
throw '-TestMoveDirection must not be the zero vector.'
|
||||||
|
}
|
||||||
if (-not $StreamingTraceOnly -and ($TestMoveSpeedCmPerSecond -ne 600 -or
|
if (-not $StreamingTraceOnly -and ($TestMoveSpeedCmPerSecond -ne 600 -or
|
||||||
|
$StreamingCollisionLookahead -ne 1 -or $TestMoveDirection -ne '0,1,0' -or
|
||||||
$CoreRegionConcurrentAdmission -ne 1 -or $StreamingResultBacklogCap -ne 256 -or
|
$CoreRegionConcurrentAdmission -ne 1 -or $StreamingResultBacklogCap -ne 256 -or
|
||||||
$Step1MaxMeshApplies -ne 0 -or $TestTeleportAfterSeconds -ne 0 -or
|
$Step1MaxMeshApplies -ne 0 -or $TestTeleportAfterSeconds -ne 0 -or
|
||||||
$TestTeleportDistanceCm -ne 0)) {
|
$TestTeleportDistanceCm -ne 0)) {
|
||||||
@@ -169,7 +182,8 @@ $RunName += switch ($NearReserveMode) {
|
|||||||
if ($EnforceCollisionGate) { $RunName += '_collision_gate' }
|
if ($EnforceCollisionGate) { $RunName += '_collision_gate' }
|
||||||
if ($StartupTrace -or $StreamingTraceOnly) { $RunName += '_trace' }
|
if ($StartupTrace -or $StreamingTraceOnly) { $RunName += '_trace' }
|
||||||
if ($StreamingTraceOnly) {
|
if ($StreamingTraceOnly) {
|
||||||
$RunName += "_move_${TestMoveSpeedCmPerSecond}_region_${CoreRegionConcurrentAdmission}_backlog_${StreamingResultBacklogCap}"
|
$DirectionTag = $TestMoveDirection.Replace(',', '_').Replace('-', 'm').Replace('+', 'p').Replace('.', 'p')
|
||||||
|
$RunName += "_move_${TestMoveSpeedCmPerSecond}_lookahead_${StreamingCollisionLookahead}_dir_${DirectionTag}_region_${CoreRegionConcurrentAdmission}_backlog_${StreamingResultBacklogCap}"
|
||||||
if ($Step1MaxMeshApplies -gt 0) { $RunName += "_applymax_$Step1MaxMeshApplies" }
|
if ($Step1MaxMeshApplies -gt 0) { $RunName += "_applymax_$Step1MaxMeshApplies" }
|
||||||
if ($TestTeleportAfterSeconds -gt 0) {
|
if ($TestTeleportAfterSeconds -gt 0) {
|
||||||
$RunName += "_teleport_${TestTeleportAfterSeconds}s_${TestTeleportDistanceCm}cm"
|
$RunName += "_teleport_${TestTeleportAfterSeconds}s_${TestTeleportDistanceCm}cm"
|
||||||
@@ -310,7 +324,8 @@ if ($StreamingTraceOnly) {
|
|||||||
$BaseArguments += '-voxel.TestMove=1'
|
$BaseArguments += '-voxel.TestMove=1'
|
||||||
$BaseArguments += "-voxel.TestMoveSpeedCmPerSecond=$TestMoveSpeedCmPerSecond"
|
$BaseArguments += "-voxel.TestMoveSpeedCmPerSecond=$TestMoveSpeedCmPerSecond"
|
||||||
$BaseArguments += '-voxel.TestMoveStartSeconds=30'
|
$BaseArguments += '-voxel.TestMoveStartSeconds=30'
|
||||||
$BaseArguments += '-voxel.TestMoveDirection=0,1,0'
|
$BaseArguments += "-voxel.TestMoveDirection=$TestMoveDirection"
|
||||||
|
$BaseArguments += "-voxel.StreamingCollisionLookahead=$StreamingCollisionLookahead"
|
||||||
if ($TestTeleportAfterSeconds -gt 0) {
|
if ($TestTeleportAfterSeconds -gt 0) {
|
||||||
$BaseArguments += "-voxel.TestTeleportAfterSeconds=$TestTeleportAfterSeconds"
|
$BaseArguments += "-voxel.TestTeleportAfterSeconds=$TestTeleportAfterSeconds"
|
||||||
$BaseArguments += "-voxel.TestTeleportDistanceCm=$TestTeleportDistanceCm"
|
$BaseArguments += "-voxel.TestTeleportDistanceCm=$TestTeleportDistanceCm"
|
||||||
@@ -447,8 +462,10 @@ if ($SharedFeatureAuditOnly) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ($StreamingTraceOnly) {
|
if ($StreamingTraceOnly) {
|
||||||
|
$ExpectedWalkDirection = "walk_direction=$TestMoveDirection"
|
||||||
if ($Text -notmatch '\[VoxelForgeCoreWorldStreamTrace\] initial_ready=1 .*walk_tick_ms_p95=.*walk_tick_ms_max=' -or
|
if ($Text -notmatch '\[VoxelForgeCoreWorldStreamTrace\] initial_ready=1 .*walk_tick_ms_p95=.*walk_tick_ms_max=' -or
|
||||||
$Text -notmatch ("\[VoxelForgeCoreWorldStreamTrace\] pass complete=1 .*walk_speed_cm_s=$TestMoveSpeedCmPerSecond walk_direction=\+Y") -or
|
$Text -notmatch ("\[VoxelForgeCoreWorldStreamTrace\] pass complete=1 .*walk_speed_cm_s=$TestMoveSpeedCmPerSecond") -or
|
||||||
|
-not $Text.Contains($ExpectedWalkDirection) -or
|
||||||
$Text -notmatch '\[VoxelForgeCoreWorldTest\] pass complete=1') {
|
$Text -notmatch '\[VoxelForgeCoreWorldTest\] pass complete=1') {
|
||||||
throw "BuildHost actor-only stream trace did not prove initial readiness and a completed walk sample. See $FinalLog."
|
throw "BuildHost actor-only stream trace did not prove initial readiness and a completed walk sample. See $FinalLog."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user