streaming: one supplemental far apply per frame when the budget is half unused (L121)
After the normal visible-apply count is reached and <50% of the 2 ms budget is spent, apply one horizon result (off during startup boost; voxel.StreamingFarApplySupplement=0 disables). 30 m/s UE, 5 pairs: far holes 4.38% -> 2.70%, far finish-to-apply p50 1.105 -> 0.114 s, worst tick 14.74 ms (<15.43), near coverage +0.3 pt, arrival whole-view -313 ms. Geometry unchanged (24/24 hashes), fast tier green. Adds trace-only RMC proxy publication timing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
161565936c
commit
5c519f5206
8 files changed
+1115
-5
No files matched your search
@@ -423,6 +423,7 @@ void RecordApplyFrame(const FApplyFrameSample& Sample)
|
||||
"\"active_tasks\":%d,\"running_workers\":%d,\"task_cap\":%d,"
|
||||
"\"pending_tiles\":%d,\"pending_results_estimate\":%d,"
|
||||
"\"results_drained\":%d,\"apply_results\":%d,\"meshes_applied\":%d,\"max_applies\":%d,"
|
||||
"\"supplemental_far_applies\":%d,"
|
||||
"\"apply_budget_ms\":%s,\"count_limit_reached\":%d,\"time_limit_reached\":%d,"
|
||||
"\"pending_by_lod\":%s,\"apply_results_by_lod\":%s,"
|
||||
"\"meshes_by_lod\":%s,\"apply_ms_by_lod\":%s"),
|
||||
@@ -432,7 +433,8 @@ void RecordApplyFrame(const FApplyFrameSample& Sample)
|
||||
Sample.RunningWorkerTasks, Sample.TaskCap, Sample.PendingTiles,
|
||||
Sample.PendingResultsEstimate, Sample.ResultsDrained, Sample.ApplyResultsByLevelTotal,
|
||||
Sample.MeshesApplied,
|
||||
Sample.MaxApplies, *Number(Sample.ApplyBudgetMilliseconds),
|
||||
Sample.MaxApplies, Sample.SupplementalFarApplies,
|
||||
*Number(Sample.ApplyBudgetMilliseconds),
|
||||
Sample.bCountLimitReached ? 1 : 0, Sample.bTimeLimitReached ? 1 : 0,
|
||||
*PendingByLevel, *ApplyResultsByLevel, *MeshesByLevel, *ApplyMillisecondsByLevel));
|
||||
}
|
||||
|
||||
@@ -268,6 +268,12 @@ namespace
|
||||
GVoxelForgeStreamingResultBacklogCap,
|
||||
TEXT("Maximum queued results plus admitted tile tasks; 0 disables the safety cap. This is separate from the worker-slot cap."));
|
||||
|
||||
int32 GVoxelForgeStreamingFarApplySupplement = 1;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingFarApplySupplement(
|
||||
TEXT("voxel.StreamingFarApplySupplement"),
|
||||
GVoxelForgeStreamingFarApplySupplement,
|
||||
TEXT("After the normal visible-apply count is reached, allow one far mesh apply when half of the frame time budget remains; 0 disables the supplemental far lane."));
|
||||
|
||||
int32 GVoxelForgeStreamingReadyQueueDepth = 64;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingReadyQueueDepth(
|
||||
TEXT("voxel.StreamingReadyQueueDepth"),
|
||||
@@ -2223,6 +2229,12 @@ void AVoxelWorld::BeginPlay()
|
||||
}
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingStartupApplyBoost="),
|
||||
GVoxelForgeStreamingStartupApplyBoost);
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingFarApplySupplement="),
|
||||
GVoxelForgeStreamingFarApplySupplement))
|
||||
{
|
||||
GVoxelForgeStreamingFarApplySupplement =
|
||||
GVoxelForgeStreamingFarApplySupplement != 0 ? 1 : 0;
|
||||
}
|
||||
// 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 ;
|
||||
@@ -2737,6 +2749,7 @@ void AVoxelWorld::BeginPlay()
|
||||
"\"streaming_collision_lookahead\":%d,\"core_region_concurrent_admission\":%d,"
|
||||
"\"collision_gate\":%d,"
|
||||
"\"streaming_result_backlog_cap\":%d,\"streaming_startup_apply_boost\":%d,"
|
||||
"\"streaming_far_apply_supplement\":%d,"
|
||||
"\"streaming_background_worker_reserve\":%d,\"core_parallel_for_background_priority\":%d,"
|
||||
"\"streaming_tile_background_low\":%d,\"streaming_tile_background_normal\":%d,"
|
||||
"\"core_column_cache_direct_mapped\":%d,"
|
||||
@@ -2758,6 +2771,7 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeStreamingCollisionLookahead, GVoxelForgeCoreRegionConcurrentAdmission,
|
||||
GVoxelForgeCollisionGate,
|
||||
GVoxelForgeStreamingResultBacklogCap, GVoxelForgeStreamingStartupApplyBoost,
|
||||
GVoxelForgeStreamingFarApplySupplement,
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve,
|
||||
GVoxelForgeCoreParallelForBackgroundPriority,
|
||||
GVoxelForgeStreamingTileBackgroundLow,
|
||||
@@ -9881,6 +9895,7 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
const double ApplyBudgetSeconds =
|
||||
static_cast<double>(ApplyBudgetMilliseconds) * 0.001;
|
||||
int32 MeshesApplied = 0;
|
||||
int32 SupplementalFarApplies = 0;
|
||||
int32 ResultsDrained = 0;
|
||||
int32 ApplyResultsByLevelTotal = 0;
|
||||
double ApplySeconds = 0.0;
|
||||
@@ -9888,6 +9903,8 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
TStaticArray<int32, 9> MeshesAppliedByLevel{};
|
||||
TStaticArray<double, 9> ApplySecondsByLevel{};
|
||||
bool bTimeBudgetHit = false;
|
||||
bool bForceSupplementalFarDequeue = false;
|
||||
bool bDequeuedSupplementalFar = false;
|
||||
const auto RecordApplyFrame = [&]()
|
||||
{
|
||||
if (!VoxelForgeStartupTrace::IsActive()) return;
|
||||
@@ -9911,6 +9928,7 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
Sample.ResultsDrained = ResultsDrained;
|
||||
Sample.ApplyResultsByLevelTotal = ApplyResultsByLevelTotal;
|
||||
Sample.MeshesApplied = MeshesApplied;
|
||||
Sample.SupplementalFarApplies = SupplementalFarApplies;
|
||||
Sample.MaxApplies = MaxApplies;
|
||||
Sample.ApplyBudgetMilliseconds = ApplyBudgetMilliseconds;
|
||||
Sample.bCountLimitReached = MeshesApplied >= MaxApplies;
|
||||
@@ -9939,6 +9957,7 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
FChunkResult DequeuedChunk;
|
||||
const auto DequeueNextResult = [&]() -> bool
|
||||
{
|
||||
bDequeuedSupplementalFar = false;
|
||||
if (bStep1UploadCorpusReleased
|
||||
&& Step1HeldApplyCursor < Step1HeldApplyResults.Num())
|
||||
{
|
||||
@@ -9954,8 +9973,19 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
// Mirror the admission order. A horizon result that completed earlier must not consume
|
||||
// the game-thread apply budget ahead of collision support, an edit, or ordinary LOD0.
|
||||
if (CriticalProcessQueue.Dequeue(DequeuedChunk)
|
||||
|| EditedProcessQueue.Dequeue(DequeuedChunk)
|
||||
|| NearProcessQueue.Dequeue(DequeuedChunk)
|
||||
|| EditedProcessQueue.Dequeue(DequeuedChunk))
|
||||
{
|
||||
RecordStreamingResultDequeued(DequeuedChunk);
|
||||
return true;
|
||||
}
|
||||
if (bForceSupplementalFarDequeue)
|
||||
{
|
||||
if (!ProcessQueue.Dequeue(DequeuedChunk)) return false;
|
||||
bDequeuedSupplementalFar = true;
|
||||
RecordStreamingResultDequeued(DequeuedChunk);
|
||||
return true;
|
||||
}
|
||||
if (NearProcessQueue.Dequeue(DequeuedChunk)
|
||||
|| ProcessQueue.Dequeue(DequeuedChunk))
|
||||
{
|
||||
RecordStreamingResultDequeued(DequeuedChunk);
|
||||
@@ -10104,6 +10134,10 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
if (bMeshApplied)
|
||||
{
|
||||
++MeshesApplied;
|
||||
if (bDequeuedSupplementalFar && ResultLevel >= 3)
|
||||
{
|
||||
++SupplementalFarApplies;
|
||||
}
|
||||
if (ResultLevel >= 0 && ResultLevel < 9)
|
||||
{
|
||||
++MeshesAppliedByLevel[ResultLevel];
|
||||
@@ -10122,6 +10156,19 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
}
|
||||
if (MeshesApplied >= MaxApplies)
|
||||
{
|
||||
const double DrainElapsedSeconds = FPlatformTime::ToSeconds64(
|
||||
FPlatformTime::Cycles64() - DrainStartCycles);
|
||||
const bool bCanSupplementFar = MeshesApplied == MaxApplies
|
||||
&& SupplementalFarApplies == 0
|
||||
&& !bUseStartupApplyBoost
|
||||
&& GVoxelForgeStreamingFarApplySupplement != 0
|
||||
&& ApplyBudgetSeconds > 0.0
|
||||
&& DrainElapsedSeconds < ApplyBudgetSeconds * 0.5;
|
||||
if (bCanSupplementFar)
|
||||
{
|
||||
bForceSupplementalFarDequeue = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -15360,6 +15407,8 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
|
||||
const int32 NumIndices = Result.NumTriangles * 3;
|
||||
const int32 FirstCapIndex = FMath::Clamp(
|
||||
NumIndices - Result.NumCeilingTriangles * 3, 0, NumIndices);
|
||||
TFuture<ERealtimeMeshProxyUpdateStatus> ProxyPublicationFuture;
|
||||
bool bHasProxyPublicationFuture = false;
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_RMC_CreateSections);
|
||||
if (bHasGroundTris)
|
||||
@@ -15369,9 +15418,10 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
|
||||
const FRealtimeMeshStreamRange GroundRange =
|
||||
URealtimeMeshBlueprintFunctionLibrary::MakeStreamRange(
|
||||
0, Result.NumVertices, 0, FirstCapIndex);
|
||||
RTMesh->CreateSection(
|
||||
ProxyPublicationFuture = RTMesh->CreateSection(
|
||||
FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0),
|
||||
GroundConfig, GroundRange, /*bShouldCreateCollision*/ bWantsCollision);
|
||||
bHasProxyPublicationFuture = true;
|
||||
}
|
||||
if (bHasCeilingTris)
|
||||
{
|
||||
@@ -15380,11 +15430,48 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
|
||||
const FRealtimeMeshStreamRange CapRange =
|
||||
URealtimeMeshBlueprintFunctionLibrary::MakeStreamRange(
|
||||
0, Result.NumVertices, FirstCapIndex, NumIndices);
|
||||
RTMesh->CreateSection(
|
||||
ProxyPublicationFuture = RTMesh->CreateSection(
|
||||
FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 1),
|
||||
CapConfig, CapRange, /*bShouldCreateCollision*/ bWantsCollision);
|
||||
bHasProxyPublicationFuture = true;
|
||||
}
|
||||
}
|
||||
const uint64 ProxyPublicationSubmitCycles = FPlatformTime::Cycles64();
|
||||
if (bTraceRmcTiming && bHasProxyPublicationFuture)
|
||||
{
|
||||
const TWeakObjectPtr<AVoxelWorld> WeakWorld(this);
|
||||
const TWeakObjectPtr<URealtimeMeshComponent> WeakComponent(MeshComp);
|
||||
const FVoxelTileKey PublishedTile = Tile;
|
||||
const uint64 RequestOrdinal = Result.RequestOrdinal;
|
||||
const uint64 ApplyStartedCycles = ApplyStartCycles;
|
||||
ProxyPublicationFuture.Next(
|
||||
[WeakWorld, WeakComponent, PublishedTile, RequestOrdinal,
|
||||
ApplyStartedCycles, ProxyPublicationSubmitCycles](
|
||||
ERealtimeMeshProxyUpdateStatus Status)
|
||||
{
|
||||
if (!VoxelForgeStartupTrace::IsActive()) return;
|
||||
const uint64 PublishedCycles = FPlatformTime::Cycles64();
|
||||
const URealtimeMeshComponent* Component = WeakComponent.Get();
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("rmc_proxy_published"), FString::Printf(
|
||||
TEXT("\"tile\":[%d,%d,%d],\"level\":%d,\"request_ordinal\":%llu,"
|
||||
"\"proxy_update_status\":%d,\"apply_to_proxy_ms\":%.3f,"
|
||||
"\"section_submit_to_proxy_ms\":%.3f,\"component_visible\":%d,"
|
||||
"\"world_valid\":%d"),
|
||||
PublishedTile.Coord.X, PublishedTile.Coord.Y, PublishedTile.Coord.Z,
|
||||
PublishedTile.Level, static_cast<unsigned long long>(RequestOrdinal),
|
||||
static_cast<int32>(Status),
|
||||
ApplyStartedCycles != 0 && PublishedCycles >= ApplyStartedCycles
|
||||
? FPlatformTime::ToSeconds64(PublishedCycles - ApplyStartedCycles) * 1000.0
|
||||
: 0.0,
|
||||
ProxyPublicationSubmitCycles != 0
|
||||
&& PublishedCycles >= ProxyPublicationSubmitCycles
|
||||
? FPlatformTime::ToSeconds64(
|
||||
PublishedCycles - ProxyPublicationSubmitCycles) * 1000.0
|
||||
: 0.0,
|
||||
Component && Component->IsVisible() ? 1 : 0,
|
||||
WeakWorld.IsValid() ? 1 : 0));
|
||||
});
|
||||
}
|
||||
if (bTraceRmcTiming)
|
||||
{
|
||||
TraceSectionConfigMilliseconds = FPlatformTime::ToSeconds64(
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace VoxelForgeStartupTrace
|
||||
int32 ResultsDrained = 0;
|
||||
int32 ApplyResultsByLevelTotal = 0;
|
||||
int32 MeshesApplied = 0;
|
||||
int32 SupplementalFarApplies = 0;
|
||||
int32 MaxApplies = 0;
|
||||
double ApplyBudgetMilliseconds = 0.0;
|
||||
bool bCountLimitReached = false;
|
||||
|
||||
Reference in new issue
Block a user