wip(assembler): round S6 -- in-game cold view counted; workers mostly idle, apply cap not the floor
Direct per-tile trace on the BuildHost actor (owner plan, 2,578 keys): full set 17.4 s legacy; gated coarse-first (after the 9-tile walkable ring) 11.7 s with the ring still at 0.63 s, but full LOD0 slips 2.3 -> 10.2 s. Startup apply boost changed nothing (0.4%). 37.6 worker-s over 17.4 s on 10 slots = ~3.3 busy: the floor is admission/work supply. S5's 251 ms staircase was the trace sampler. Fixed CVar parsing in headless BeginPlay and the 60 m/s crossing instrument. All three knobs stay opt-in. 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
c0596c5f24
commit
b9c13c37c0
11 files changed
+903
-31
No files matched your search
@@ -237,6 +237,15 @@ namespace
|
||||
GVoxelForgeStep1MaxMeshApplies,
|
||||
TEXT("Step 1 measurement override: 0 uses the asset MaxMeshAppliesPerFrame, otherwise caps visible applies per frame."));
|
||||
|
||||
int32 GVoxelForgeStreamingStartupApplyBoost = 0;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingStartupApplyBoost(
|
||||
TEXT("voxel.StreamingStartupApplyBoost"),
|
||||
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."));
|
||||
constexpr int32 StreamingStartupMaxMeshApplies = 12;
|
||||
constexpr float StreamingStartupApplyBudgetMilliseconds = 14.0f;
|
||||
constexpr double StreamingStartupApplyMaxSeconds = 30.0;
|
||||
|
||||
int32 GVoxelForgeStep1PrequeueVisibleLOD0 = 0;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStep1PrequeueVisibleLOD0(
|
||||
TEXT("voxel.Step1PrequeueVisibleLOD0"),
|
||||
@@ -2041,6 +2050,16 @@ void AVoxelWorld::BeginPlay()
|
||||
{
|
||||
GVoxelForgeStep1MaxMeshApplies = CommandLineStep1MaxMeshApplies;
|
||||
}
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingStartupApplyBoost="),
|
||||
GVoxelForgeStreamingStartupApplyBoost);
|
||||
// Headless BuildHost does not reliably apply arbitrary console-variable command-line tokens
|
||||
// before BeginPlay; parse the test switches explicitly so A/B runs exercise the requested path.
|
||||
// BuildHost sans interface n'applique pas toujours les CVar arbitraires avant BeginPlay ;
|
||||
// lire explicitement ces options afin que les essais A/B utilisent bien le chemin demande.
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingCoarseFirst="),
|
||||
GVoxelForgeStreamingCoarseFirst);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingDynamicNearReserve="),
|
||||
GVoxelForgeStreamingDynamicNearReserve);
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.Step1PrequeueVisibleLOD0="),
|
||||
CommandLineStep1PrequeueVisibleLOD0))
|
||||
{
|
||||
@@ -3026,9 +3045,26 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
|
||||
if (bMoveRequested && !bHeadlessStrateCrossingTest)
|
||||
{
|
||||
// FParse::Value treats commas as separators in some commandlet argument builds; read the
|
||||
// whole vector token so the requested X,Y,Z direction survives intact.
|
||||
// FParse::Value separe parfois les virgules dans les commandes ; lire le jeton X,Y,Z complet.
|
||||
FString DirectionText;
|
||||
if (FParse::Value(CommandLine, TEXT("voxel.TestMoveDirection="), DirectionText))
|
||||
const FString CommandLineText(CommandLine);
|
||||
const FString DirectionKey(TEXT("voxel.TestMoveDirection="));
|
||||
const int32 DirectionKeyIndex = CommandLineText.Find(
|
||||
DirectionKey, ESearchCase::IgnoreCase, ESearchDir::FromStart);
|
||||
if (DirectionKeyIndex != INDEX_NONE)
|
||||
{
|
||||
DirectionText = CommandLineText.Mid(DirectionKeyIndex + DirectionKey.Len());
|
||||
const int32 DirectionEnd = DirectionText.Find(
|
||||
TEXT(" "), ESearchCase::CaseSensitive, ESearchDir::FromStart);
|
||||
if (DirectionEnd != INDEX_NONE)
|
||||
{
|
||||
DirectionText.LeftInline(DirectionEnd, EAllowShrinking::No);
|
||||
}
|
||||
DirectionText.TrimStartAndEndInline();
|
||||
DirectionText.RemoveFromStart(TEXT("\""));
|
||||
DirectionText.RemoveFromEnd(TEXT("\""));
|
||||
TArray<FString> Components;
|
||||
DirectionText.ParseIntoArray(Components, TEXT(","), true);
|
||||
if (Components.Num() == 3)
|
||||
@@ -6717,6 +6753,7 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
AdvanceHeadlessDigMotionTest(PlayerLastPos, PlayerPawn);
|
||||
PeakObservedPawnSpeedCmPerSecond = FMath::Max(
|
||||
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size2D());
|
||||
StartupTraceSpeedCmPerSecond = PlayerHeading.Size2D();
|
||||
UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime);
|
||||
RecordHeadlessDigMotionFrame(PlayerPawn, PlayerLastPos, DeltaTime);
|
||||
ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn);
|
||||
@@ -9064,19 +9101,121 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
// 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.
|
||||
const bool bProfileApply = GVoxelForgeProfileTileGeneration != 0;
|
||||
uint64 DrainStartCycles = FPlatformTime::Cycles64();
|
||||
const uint64 ApplyFrameStartCycles = FPlatformTime::Cycles64();
|
||||
const double FrameSeconds = LastApplyFrameCycles != 0
|
||||
&& ApplyFrameStartCycles >= LastApplyFrameCycles
|
||||
? FPlatformTime::ToSeconds64(ApplyFrameStartCycles - LastApplyFrameCycles)
|
||||
: 0.0;
|
||||
LastApplyFrameCycles = ApplyFrameStartCycles;
|
||||
|
||||
const double StartupElapsedSeconds = DesiredEpochStartCycles != 0
|
||||
&& ApplyFrameStartCycles >= DesiredEpochStartCycles
|
||||
? FPlatformTime::ToSeconds64(ApplyFrameStartCycles - DesiredEpochStartCycles)
|
||||
: -1.0;
|
||||
const bool bInitialDesiredSetReady = DesiredEpoch == 1
|
||||
&& bAllChunksLoaded
|
||||
&& PendingTiles.Num() == 0
|
||||
&& PendingCollisionCooks.Num() == 0
|
||||
&& LastLOD0RingReadyEpoch == DesiredEpoch;
|
||||
if (bStartupApplyBoostStarted && !bStartupApplyBoostEnded
|
||||
&& (bInitialDesiredSetReady || DesiredEpoch != 1
|
||||
|| GVoxelForgeStreamingStartupApplyBoost <= 0
|
||||
|| GVoxelForgeStep1MaxMeshApplies > 0
|
||||
|| StartupElapsedSeconds >= StreamingStartupApplyMaxSeconds))
|
||||
{
|
||||
bStartupApplyBoostEnded = true;
|
||||
if (VoxelForgeStartupTrace::IsActive())
|
||||
{
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("startup_apply_budget"), FString::Printf(
|
||||
TEXT("\"state\":\"normal\",\"reason\":\"%s\",\"elapsed_s\":%s"),
|
||||
bInitialDesiredSetReady ? TEXT("initial_set_and_walkable_ring_ready")
|
||||
: (StartupElapsedSeconds >= StreamingStartupApplyMaxSeconds
|
||||
? TEXT("time_limit")
|
||||
: (DesiredEpoch != 1 ? TEXT("desired_epoch_changed")
|
||||
: TEXT("override_disabled"))),
|
||||
*FString::SanitizeFloat(StartupElapsedSeconds)));
|
||||
}
|
||||
}
|
||||
const bool bUseStartupApplyBoost = GVoxelForgeStreamingStartupApplyBoost > 0
|
||||
&& GVoxelForgeStep1MaxMeshApplies <= 0
|
||||
&& !bStartupApplyBoostEnded
|
||||
&& DesiredEpoch == 1
|
||||
&& StartupElapsedSeconds >= 0.0
|
||||
&& !bInitialDesiredSetReady
|
||||
&& StartupElapsedSeconds < StreamingStartupApplyMaxSeconds;
|
||||
if (bUseStartupApplyBoost && !bStartupApplyBoostStarted)
|
||||
{
|
||||
bStartupApplyBoostStarted = true;
|
||||
if (VoxelForgeStartupTrace::IsActive())
|
||||
{
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("startup_apply_budget"), FString::Printf(
|
||||
TEXT("\"state\":\"boosted\",\"max_applies\":%d,\"budget_ms\":%.3f,"
|
||||
"\"max_duration_s\":%.3f,\"desired_epoch\":%u"),
|
||||
StreamingStartupMaxMeshApplies,
|
||||
StreamingStartupApplyBudgetMilliseconds,
|
||||
StreamingStartupApplyMaxSeconds,
|
||||
DesiredEpoch));
|
||||
}
|
||||
}
|
||||
|
||||
uint64 DrainStartCycles = ApplyFrameStartCycles;
|
||||
const int32 ConfiguredMaxApplies = FMath::Max(
|
||||
Settings ? Settings->MaxMeshAppliesPerFrame : 4, 1);
|
||||
const int32 MaxApplies = GVoxelForgeStep1MaxMeshApplies > 0
|
||||
? GVoxelForgeStep1MaxMeshApplies : ConfiguredMaxApplies;
|
||||
const float ApplyBudgetMilliseconds = Settings
|
||||
? FMath::Max(Settings->MaxMeshApplyMilliseconds, 0.0f)
|
||||
: 2.0f;
|
||||
? GVoxelForgeStep1MaxMeshApplies
|
||||
: (bUseStartupApplyBoost ? StreamingStartupMaxMeshApplies : ConfiguredMaxApplies);
|
||||
const float ApplyBudgetMilliseconds = bUseStartupApplyBoost
|
||||
? StreamingStartupApplyBudgetMilliseconds
|
||||
: (Settings ? FMath::Max(Settings->MaxMeshApplyMilliseconds, 0.0f) : 2.0f);
|
||||
const double ApplyBudgetSeconds =
|
||||
static_cast<double>(ApplyBudgetMilliseconds) * 0.001;
|
||||
int32 MeshesApplied = 0;
|
||||
int32 ResultsDrained = 0;
|
||||
int32 ApplyResultsByLevelTotal = 0;
|
||||
double ApplySeconds = 0.0;
|
||||
TStaticArray<int32, 9> ApplyResultsByLevel{};
|
||||
TStaticArray<int32, 9> MeshesAppliedByLevel{};
|
||||
TStaticArray<double, 9> ApplySecondsByLevel{};
|
||||
bool bTimeBudgetHit = false;
|
||||
const auto RecordApplyFrame = [&]()
|
||||
{
|
||||
if (!VoxelForgeStartupTrace::IsActive()) return;
|
||||
VoxelForgeStartupTrace::FApplyFrameSample Sample;
|
||||
Sample.Frame = GFrameCounter;
|
||||
Sample.FrameSeconds = FrameSeconds;
|
||||
Sample.SpeedCmPerSecond = StartupTraceSpeedCmPerSecond;
|
||||
Sample.ApplySeconds = ApplySeconds;
|
||||
Sample.DrainSeconds = FPlatformTime::ToSeconds64(
|
||||
FPlatformTime::Cycles64() - DrainStartCycles);
|
||||
// `GetActiveGenerationTaskCount` includes completed tiles until the game-thread apply removes
|
||||
// their PendingTiles entry. Use the task guard's counter here so the difference estimates
|
||||
// results waiting in the MPSC queues instead of reporting zero by construction.
|
||||
// `GetActiveGenerationTaskCount` inclut les resultats termines jusqu'a leur application.
|
||||
// Utiliser le compteur RAII pour mesurer les resultats en attente dans les files MPSC.
|
||||
Sample.ActiveTasks = ActiveTaskCount.load(std::memory_order_relaxed);
|
||||
Sample.RunningWorkerTasks = ActiveGenerationWorkerCount.load(std::memory_order_relaxed);
|
||||
Sample.TaskCap = GetMaxConcurrentTasks();
|
||||
Sample.PendingTiles = PendingTiles.Num();
|
||||
Sample.PendingResultsEstimate = FMath::Max(0, Sample.PendingTiles - Sample.ActiveTasks);
|
||||
Sample.ResultsDrained = ResultsDrained;
|
||||
Sample.ApplyResultsByLevelTotal = ApplyResultsByLevelTotal;
|
||||
Sample.MeshesApplied = MeshesApplied;
|
||||
Sample.MaxApplies = MaxApplies;
|
||||
Sample.ApplyBudgetMilliseconds = ApplyBudgetMilliseconds;
|
||||
Sample.bCountLimitReached = MeshesApplied >= MaxApplies;
|
||||
Sample.bTimeLimitReached = bTimeBudgetHit;
|
||||
Sample.ApplyResultsByLevel = ApplyResultsByLevel;
|
||||
Sample.MeshesAppliedByLevel = MeshesAppliedByLevel;
|
||||
Sample.ApplySecondsByLevel = ApplySecondsByLevel;
|
||||
for (const FVoxelTileKey& PendingTile : PendingTiles)
|
||||
{
|
||||
if (PendingTile.Level >= 0 && PendingTile.Level < 9)
|
||||
{
|
||||
++Sample.PendingByLevel[PendingTile.Level];
|
||||
}
|
||||
}
|
||||
VoxelForgeStartupTrace::RecordApplyFrame(Sample);
|
||||
};
|
||||
FChunkResult DequeuedChunk;
|
||||
const auto DequeueNextResult = [&]() -> bool
|
||||
{
|
||||
@@ -9177,6 +9316,7 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
Step1HeldApplyResults.Num(), GetActiveGenerationTaskCount(), PendingTiles.Num());
|
||||
Step1LastLoggedPrequeueCount = Step1HeldVisibleLOD0Count;
|
||||
}
|
||||
RecordApplyFrame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -9222,9 +9362,27 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
// ApplyTileResult does epoch check, capture ingest, empty-release / mesh submission, and
|
||||
// only then records the tile as loaded. Only a real (visible) upload counts against the
|
||||
// per-frame budget — stale/empty drains are free.
|
||||
if (ApplyTileResult(DequeuedChunk))
|
||||
const int32 ResultLevel = DequeuedChunk.Tile.Level;
|
||||
const uint64 TileApplyStartCycles = FPlatformTime::Cycles64();
|
||||
const bool bMeshApplied = ApplyTileResult(DequeuedChunk);
|
||||
const uint64 TileApplyEndCycles = FPlatformTime::Cycles64();
|
||||
const double TileApplySeconds = TileApplyEndCycles >= TileApplyStartCycles
|
||||
? FPlatformTime::ToSeconds64(TileApplyEndCycles - TileApplyStartCycles)
|
||||
: 0.0;
|
||||
ApplySeconds += TileApplySeconds;
|
||||
if (ResultLevel >= 0 && ResultLevel < 9)
|
||||
{
|
||||
++ApplyResultsByLevelTotal;
|
||||
++ApplyResultsByLevel[ResultLevel];
|
||||
ApplySecondsByLevel[ResultLevel] += TileApplySeconds;
|
||||
}
|
||||
if (bMeshApplied)
|
||||
{
|
||||
++MeshesApplied;
|
||||
if (ResultLevel >= 0 && ResultLevel < 9)
|
||||
{
|
||||
++MeshesAppliedByLevel[ResultLevel];
|
||||
}
|
||||
}
|
||||
|
||||
// Time-box the entire game-thread drain, not only visible uploads. Empty releases and
|
||||
@@ -9255,6 +9413,7 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
ApplyBudgetMilliseconds,
|
||||
bTimeBudgetHit ? 1 : 0);
|
||||
}
|
||||
RecordApplyFrame();
|
||||
}
|
||||
|
||||
// Game-thread apply for one gen result. Shared by ProcessPendingChunks (async drain) and
|
||||
@@ -9379,31 +9538,44 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
|
||||
const uint64 AppliedCycles = FPlatformTime::Cycles64();
|
||||
const bool bHaveWorkerTiming = Result.GenerationStartCycles != 0
|
||||
&& Result.GenerationEndCycles >= Result.GenerationStartCycles;
|
||||
const bool bHaveWorkerTaskTiming = Result.WorkerTaskStartCycles != 0
|
||||
&& Result.WorkerTaskEndCycles >= Result.WorkerTaskStartCycles;
|
||||
const double RequestToApply = FPlatformTime::ToSeconds64(
|
||||
AppliedCycles - Result.RequestStartCycles);
|
||||
const double ApplySeconds = ApplyStartCycles != 0
|
||||
? FPlatformTime::ToSeconds64(AppliedCycles - ApplyStartCycles) : 0.0;
|
||||
const double WorkerTaskSeconds = bHaveWorkerTaskTiming
|
||||
? FPlatformTime::ToSeconds64(
|
||||
Result.WorkerTaskEndCycles - Result.WorkerTaskStartCycles)
|
||||
: (bHaveWorkerTiming ? FPlatformTime::ToSeconds64(
|
||||
Result.GenerationEndCycles - Result.GenerationStartCycles) : 0.0);
|
||||
const double GenerationSeconds = bHaveWorkerTiming
|
||||
? FPlatformTime::ToSeconds64(
|
||||
Result.GenerationEndCycles - Result.GenerationStartCycles) : 0.0;
|
||||
const double WorkerQueueSeconds = bHaveWorkerTiming
|
||||
? FPlatformTime::ToSeconds64(
|
||||
Result.GenerationStartCycles - Result.RequestStartCycles) : 0.0;
|
||||
const double ResultQueueSeconds = bHaveWorkerTiming
|
||||
(bHaveWorkerTaskTiming ? Result.WorkerTaskStartCycles
|
||||
: Result.GenerationStartCycles) - Result.RequestStartCycles) : 0.0;
|
||||
const uint64 WorkerTaskEndCycles = bHaveWorkerTaskTiming
|
||||
? Result.WorkerTaskEndCycles : Result.GenerationEndCycles;
|
||||
const double ResultQueueSeconds = bHaveWorkerTiming && ApplyStartCycles != 0
|
||||
&& ApplyStartCycles >= WorkerTaskEndCycles
|
||||
? FPlatformTime::ToSeconds64(ApplyStartCycles - WorkerTaskEndCycles)
|
||||
: (bHaveWorkerTiming
|
||||
? FMath::Max(0.0, RequestToApply - WorkerQueueSeconds
|
||||
- GenerationSeconds - ApplySeconds)
|
||||
: FMath::Max(0.0, RequestToApply - ApplySeconds);
|
||||
- WorkerTaskSeconds - ApplySeconds)
|
||||
: FMath::Max(0.0, RequestToApply - ApplySeconds));
|
||||
if (GVoxelForgeProfileTileGeneration != 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTileApplied] tile=(%d,%d,%d) level=%d empty=%d triangles=%d "
|
||||
"band=(%d,%d) "
|
||||
"request_to_apply=%.6f worker_queue=%.6f generation=%.6f "
|
||||
"request_to_apply=%.6f worker_queue=%.6f worker_task=%.6f generation=%.6f "
|
||||
"result_queue=%.6f apply=%.6f"),
|
||||
Result.Tile.Coord.X, Result.Tile.Coord.Y, Result.Tile.Coord.Z,
|
||||
Result.Tile.Level, bResultEmpty ? 1 : 0, Result.NumTriangles,
|
||||
Result.BandChunkLo, Result.BandChunkHi,
|
||||
RequestToApply, WorkerQueueSeconds, GenerationSeconds,
|
||||
RequestToApply, WorkerQueueSeconds, WorkerTaskSeconds, GenerationSeconds,
|
||||
ResultQueueSeconds, ApplySeconds);
|
||||
}
|
||||
if (bTraceTile)
|
||||
@@ -9427,6 +9599,7 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
|
||||
Sample.WorkerQueueSeconds = WorkerQueueSeconds;
|
||||
Sample.ResultQueueSeconds = ResultQueueSeconds;
|
||||
Sample.QueueWaitSeconds = WorkerQueueSeconds + ResultQueueSeconds;
|
||||
Sample.WorkerTaskSeconds = WorkerTaskSeconds;
|
||||
Sample.GenerationSeconds = GenerationSeconds;
|
||||
Sample.ClassifySeconds = Result.ClassifySeconds;
|
||||
Sample.MeshSeconds = Result.MeshSeconds;
|
||||
@@ -11307,7 +11480,12 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
// refinement. The opt-out reproduces the S4 LOD0-to-LOD8 submission order for a control.
|
||||
// Le shell lointain passe apres l'anneau de collision, puis le raffinement fin. L'option
|
||||
// inverse restaure l'ordre LOD0-vers-LOD8 de S4 comme controle negatif.
|
||||
if (GVoxelForgeStreamingCoarseFirst != 0)
|
||||
// Keep the walkable ring on the legacy near-first path; only spend its reserved task slots
|
||||
// on coarse work after all nine collision gates are ready.
|
||||
// Garder l'anneau praticable en priorite ; les taches grossieres commencent apres ses neuf collisions.
|
||||
const bool bUseCoarseFirstOrder = GVoxelForgeStreamingCoarseFirst != 0
|
||||
&& bWalkableRingReadyForReserve;
|
||||
if (bUseCoarseFirstOrder)
|
||||
{
|
||||
const int32 FirstCoarseLevel = FMath::Max(
|
||||
2, Settings ? Settings->FullResClipLevels : 2);
|
||||
@@ -11426,7 +11604,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
TEXT("\"scope\":\"desired_residency_not_visual_enclosure\",\"center\":[%d,%d,%d],"
|
||||
"\"position_cm\":[%s,%s,%s],\"speed_cm_s\":%s,\"walkable_ready\":%d,"
|
||||
"\"desired_by_lod\":%s,\"resident_by_lod\":%s,\"pending_by_lod\":%s,"
|
||||
"\"active_generation\":%d,\"task_cap\":%d,\"near_reserve\":%d,\"epoch\":%u,"
|
||||
"\"active_generation\":%d,\"task_cap\":%d,\"near_reserve\":%d,"
|
||||
"\"coarse_first\":%d,\"dynamic_near_reserve\":%d,\"epoch\":%u,"
|
||||
"\"update_chunks_ms\":%s,\"desired_rebuild_ms\":%s"),
|
||||
CenterChunk.X, CenterChunk.Y, CenterChunk.Z,
|
||||
*FString::SanitizeFloat(LocalPawn.X), *FString::SanitizeFloat(LocalPawn.Y),
|
||||
@@ -11434,7 +11613,9 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
*FString::SanitizeFloat(PlayerHeading.Size2D()),
|
||||
bWalkableRingReadyForReserve ? 1 : 0,
|
||||
*DesiredCounts, *ResidentCounts, *PendingCounts,
|
||||
GetActiveGenerationTaskCount(), MaxTasks, NearTaskReserve, GenerationEpoch,
|
||||
GetActiveGenerationTaskCount(), MaxTasks, NearTaskReserve,
|
||||
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
|
||||
GenerationEpoch,
|
||||
*FString::SanitizeFloat(StreamingUpdateMilliseconds),
|
||||
*FString::SanitizeFloat(DesiredRebuildMilliseconds)));
|
||||
LastStreamingTraceSampleSeconds = TraceNow;
|
||||
@@ -11626,8 +11807,16 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
|
||||
std::atomic<int32>& Counter;
|
||||
~FTaskGuard() { Counter.fetch_sub(1, std::memory_order_relaxed); }
|
||||
} Guard{ActiveTaskCount};
|
||||
ActiveGenerationWorkerCount.fetch_add(1, std::memory_order_relaxed);
|
||||
const uint64 WorkerTaskStartCycles = FPlatformTime::Cycles64();
|
||||
struct FWorkerGuard
|
||||
{
|
||||
std::atomic<int32>& Counter;
|
||||
~FWorkerGuard() { Counter.fetch_sub(1, std::memory_order_relaxed); }
|
||||
} WorkerGuard{ActiveGenerationWorkerCount};
|
||||
|
||||
FChunkResult Result;
|
||||
Result.WorkerTaskStartCycles = WorkerTaskStartCycles;
|
||||
Result.RequestOrdinal = RequestOrdinal;
|
||||
Result.bResidentAtRequest = bResidentAtRequest;
|
||||
Result.WorkPriority = WorkPriority;
|
||||
@@ -11647,6 +11836,8 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
|
||||
Canceled.RequestStartCycles = RequestStartCycles;
|
||||
Canceled.RequestOrdinal = RequestOrdinal;
|
||||
Canceled.GenerationOrdinal = Result.GenerationOrdinal;
|
||||
Canceled.WorkerTaskStartCycles = Result.WorkerTaskStartCycles;
|
||||
Canceled.WorkerTaskEndCycles = FPlatformTime::Cycles64();
|
||||
Canceled.GenerationStartCycles = Result.GenerationStartCycles;
|
||||
Canceled.GenerationEndCycles = FPlatformTime::Cycles64();
|
||||
Canceled.bResidentAtRequest = bResidentAtRequest;
|
||||
@@ -11723,6 +11914,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
|
||||
|
||||
if (!ShouldAbortWork())
|
||||
{
|
||||
Result.WorkerTaskEndCycles = FPlatformTime::Cycles64();
|
||||
if (WorkPriority == EVoxelTileWorkPriority::CollisionCritical)
|
||||
CriticalProcessQueue.Enqueue(MoveTemp(Result));
|
||||
else if (WorkPriority == EVoxelTileWorkPriority::Edited)
|
||||
@@ -11790,9 +11982,17 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
|
||||
std::atomic<int32>& Counter;
|
||||
~FTaskGuard() { Counter.fetch_sub(1, std::memory_order_relaxed); }
|
||||
} Guard{ActiveTaskCount};
|
||||
ActiveGenerationWorkerCount.fetch_add(1, std::memory_order_relaxed);
|
||||
const uint64 WorkerTaskStartCycles = FPlatformTime::Cycles64();
|
||||
struct FWorkerGuard
|
||||
{
|
||||
std::atomic<int32>& Counter;
|
||||
~FWorkerGuard() { Counter.fetch_sub(1, std::memory_order_relaxed); }
|
||||
} WorkerGuard{ActiveGenerationWorkerCount};
|
||||
|
||||
auto Enqueue = [this, WorkPriority](FChunkResult&& Result)
|
||||
{
|
||||
Result.WorkerTaskEndCycles = FPlatformTime::Cycles64();
|
||||
if (WorkPriority == EVoxelTileWorkPriority::CollisionCritical)
|
||||
CriticalProcessQueue.Enqueue(MoveTemp(Result));
|
||||
else if (WorkPriority == EVoxelTileWorkPriority::Edited)
|
||||
@@ -11810,6 +12010,7 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
|
||||
}
|
||||
|
||||
FChunkResult Result;
|
||||
Result.WorkerTaskStartCycles = WorkerTaskStartCycles;
|
||||
Result.Tile = Tile;
|
||||
Result.Epoch = TaskEpoch;
|
||||
Result.DesiredEpoch = TaskDesiredEpoch;
|
||||
@@ -11831,6 +12032,7 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
|
||||
Canceled.RequestStartCycles = RequestStartCycles;
|
||||
Canceled.RequestOrdinal = RequestOrdinal;
|
||||
Canceled.GenerationOrdinal = Result.GenerationOrdinal;
|
||||
Canceled.WorkerTaskStartCycles = Result.WorkerTaskStartCycles;
|
||||
Canceled.bResidentAtRequest = bResidentAtRequest;
|
||||
Canceled.GenerationStartCycles = Result.GenerationStartCycles;
|
||||
Canceled.GenerationEndCycles = FPlatformTime::Cycles64();
|
||||
|
||||
Reference in new issue
Block a user