unreal: tile generation at BackgroundLow priority (L97); FI arrival world-tick max 93 -> 21 ms
The 50-90 ms UWorld::Tick spikes were the TG_EndPhysics barrier waiting for physics-scene completion while all 8 TaskGraph workers ran BackgroundHigh tile tasks. Near and horizon tile generation now run at ETaskPriority::BackgroundLow (helper lanes background); queue depth and the 8 workers are kept. FI: near set 2.11 s (10 runs), max UWorld::Tick 21 ms, max EndPhysics->PostPhysics gap 16 ms; SW: 1.32 s, 11 ms. A hard 7-task cap removed spikes but doubled settle time. World unchanged. Fast tier PASS; Linux UE host builds. R44 notes (exact passage restructuring: only a full-composer z-column SIMD kernel could plausibly reach 3x; smaller exact pieces ~1.1-1.2x each) attached. 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
94f9aa6790
commit
9d9504ae4e
4 files changed
+600
-14
No files matched your search
@@ -82,6 +82,22 @@
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
static int32 GVoxelForgeCoreParallelForBackgroundPriority = 1;
|
||||
static FAutoConsoleVariableRef CVarVoxelForgeCoreParallelForBackgroundPriority(
|
||||
TEXT("voxel.CoreParallelForBackgroundPriority"),
|
||||
GVoxelForgeCoreParallelForBackgroundPriority,
|
||||
TEXT("Run Core tile helper lanes at background priority so their work yields to frame tasks; 0 restores inherited ParallelFor priority."));
|
||||
static int32 GVoxelForgeStreamingTileBackgroundLow = 1;
|
||||
static FAutoConsoleVariableRef CVarVoxelForgeStreamingTileBackgroundLow(
|
||||
TEXT("voxel.StreamingTileBackgroundLow"),
|
||||
GVoxelForgeStreamingTileBackgroundLow,
|
||||
TEXT("Run near and horizon tile generation at BackgroundLow so higher-priority engine work is scheduled first."));
|
||||
static int32 GVoxelForgeStreamingTileBackgroundNormal = 0;
|
||||
static FAutoConsoleVariableRef CVarVoxelForgeStreamingTileBackgroundNormal(
|
||||
TEXT("voxel.StreamingTileBackgroundNormal"),
|
||||
GVoxelForgeStreamingTileBackgroundNormal,
|
||||
TEXT("Run near and horizon tile generation at BackgroundNormal; BackgroundLow takes precedence."));
|
||||
|
||||
static FORCEINLINE int32 VF_ClipLevelScale(int32 Level)
|
||||
{
|
||||
const uint32 SafeLevel = static_cast<uint32>(FMath::Clamp(Level, 0, 8));
|
||||
@@ -119,6 +135,10 @@ public:
|
||||
|
||||
// Each ParallelFor lane is a stable Core worker index. Unreal owns the actual threads;
|
||||
// callbacks keep claiming fixed-size batches until every input index has run once.
|
||||
const EParallelForFlags ParallelFlags =
|
||||
GVoxelForgeCoreParallelForBackgroundPriority != 0
|
||||
? EParallelForFlags::BackgroundPriority
|
||||
: EParallelForFlags::None;
|
||||
ParallelFor(static_cast<int32>(WorkerCount), [&](int32 Lane)
|
||||
{
|
||||
const double Started = FPlatformTime::Seconds();
|
||||
@@ -134,7 +154,7 @@ public:
|
||||
}
|
||||
ActiveSeconds[static_cast<std::size_t>(Lane)] =
|
||||
FPlatformTime::Seconds() - Started;
|
||||
});
|
||||
}, ParallelFlags);
|
||||
|
||||
double TotalSeconds = 0.0;
|
||||
for (std::size_t Lane = 0; Lane < WorkerCount; ++Lane)
|
||||
@@ -260,6 +280,12 @@ namespace
|
||||
GVoxelForgeStreamingBackgroundWorkerCap,
|
||||
TEXT("Size tile admission slots to the TaskGraph background workers used by tile tasks; 0 restores the logical-core cap."));
|
||||
|
||||
int32 GVoxelForgeStreamingBackgroundWorkerReserve = 0;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingBackgroundWorkerReserve(
|
||||
TEXT("voxel.StreamingBackgroundWorkerReserve"),
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve,
|
||||
TEXT("Keep this many TaskGraph background workers outside tile admission when background worker capping is enabled."));
|
||||
|
||||
int32 GVoxelForgeCoreColumnCacheDirectMapped = 0;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeCoreColumnCacheDirectMapped(
|
||||
TEXT("voxel.CoreColumnCacheDirectMapped"),
|
||||
@@ -2121,6 +2147,13 @@ void AVoxelWorld::BeginPlay()
|
||||
int32 CommandLineStreamingResultBacklogCap = GVoxelForgeStreamingResultBacklogCap;
|
||||
int32 CommandLineStreamingReadyQueueDepth = GVoxelForgeStreamingReadyQueueDepth;
|
||||
int32 CommandLineStreamingBackgroundWorkerCap = GVoxelForgeStreamingBackgroundWorkerCap;
|
||||
int32 CommandLineStreamingBackgroundWorkerReserve =
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve;
|
||||
int32 CommandLineCoreParallelForBackgroundPriority =
|
||||
GVoxelForgeCoreParallelForBackgroundPriority;
|
||||
int32 CommandLineStreamingTileBackgroundLow = GVoxelForgeStreamingTileBackgroundLow;
|
||||
int32 CommandLineStreamingTileBackgroundNormal = GVoxelForgeStreamingTileBackgroundNormal;
|
||||
int32 CommandLineTestLogTickFunctions = 0;
|
||||
int32 CommandLineCoreColumnCacheDirectMapped = GVoxelForgeCoreColumnCacheDirectMapped;
|
||||
int32 CommandLineCoreMCGradientMemoization = GVoxelForgeCoreMCGradientMemoization;
|
||||
int32 CommandLineStep1GenerationCap = GVoxelForgeStep1GenerationCap;
|
||||
@@ -2229,6 +2262,44 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeStreamingBackgroundWorkerCap =
|
||||
CommandLineStreamingBackgroundWorkerCap != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingBackgroundWorkerReserve="),
|
||||
CommandLineStreamingBackgroundWorkerReserve))
|
||||
{
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve =
|
||||
FMath::Max(0, CommandLineStreamingBackgroundWorkerReserve);
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreParallelForBackgroundPriority="),
|
||||
CommandLineCoreParallelForBackgroundPriority))
|
||||
{
|
||||
GVoxelForgeCoreParallelForBackgroundPriority =
|
||||
CommandLineCoreParallelForBackgroundPriority != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingTileBackgroundLow="),
|
||||
CommandLineStreamingTileBackgroundLow))
|
||||
{
|
||||
GVoxelForgeStreamingTileBackgroundLow =
|
||||
CommandLineStreamingTileBackgroundLow != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingTileBackgroundNormal="),
|
||||
CommandLineStreamingTileBackgroundNormal))
|
||||
{
|
||||
GVoxelForgeStreamingTileBackgroundNormal =
|
||||
CommandLineStreamingTileBackgroundNormal != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.TestLogTickFunctions="),
|
||||
CommandLineTestLogTickFunctions)
|
||||
&& CommandLineTestLogTickFunctions != 0)
|
||||
{
|
||||
if (IConsoleVariable* LogTicks = IConsoleManager::Get().FindConsoleVariable(TEXT("tick.LogTicks")))
|
||||
{
|
||||
LogTicks->Set(1, ECVF_SetByCode);
|
||||
}
|
||||
if (IConsoleVariable* ShowPrerequisites =
|
||||
IConsoleManager::Get().FindConsoleVariable(TEXT("tick.ShowPrerequistes")))
|
||||
{
|
||||
ShowPrerequisites->Set(0, ECVF_SetByCode);
|
||||
}
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreColumnCacheDirectMapped="),
|
||||
CommandLineCoreColumnCacheDirectMapped))
|
||||
{
|
||||
@@ -2659,13 +2730,15 @@ void AVoxelWorld::BeginPlay()
|
||||
"\"taskgraph_background_threads\":%d,\"global_queued_thread_pool_threads\":%d,"
|
||||
"\"step1_cook_pool_threads\":%d,\"ready_queue_depth_cvar\":%d,"
|
||||
"\"ready_queue_depth\":%d,"
|
||||
"\"near_priority\":\"BackgroundHigh\","
|
||||
"\"horizon_priority\":\"BackgroundNormal\","
|
||||
"\"near_priority\":\"%s\","
|
||||
"\"horizon_priority\":\"%s\","
|
||||
"\"streaming_coarse_first\":%d,\"streaming_dynamic_near_reserve\":%d,"
|
||||
"\"streaming_legacy_pending_result_cap\":%d,"
|
||||
"\"streaming_collision_lookahead\":%d,\"core_region_concurrent_admission\":%d,"
|
||||
"\"collision_gate\":%d,"
|
||||
"\"streaming_result_backlog_cap\":%d,\"streaming_startup_apply_boost\":%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,"
|
||||
"\"core_mc_gradient_memoization\":%d"),
|
||||
FPlatformMisc::NumberOfCoresIncludingHyperthreads(), GetMaxConcurrentStreamingTasks(),
|
||||
@@ -2676,17 +2749,26 @@ void AVoxelWorld::BeginPlay()
|
||||
TaskGraph.GetNumBackgroundThreads(), GThreadPool ? GThreadPool->GetNumThreads() : 0,
|
||||
GVoxelForgeStep1CoreCookPool.Pool ? GVoxelForgeStep1CoreCookPool.Pool->GetNumThreads() : 0,
|
||||
GVoxelForgeStreamingReadyQueueDepth, QueueDepth,
|
||||
GVoxelForgeStreamingTileBackgroundLow ? TEXT("BackgroundLow")
|
||||
: (GVoxelForgeStreamingTileBackgroundNormal ? TEXT("BackgroundNormal")
|
||||
: TEXT("BackgroundHigh")),
|
||||
GVoxelForgeStreamingTileBackgroundLow ? TEXT("BackgroundLow") : TEXT("BackgroundNormal"),
|
||||
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
|
||||
GVoxelForgeStreamingLegacyPendingResultCap,
|
||||
GVoxelForgeStreamingCollisionLookahead, GVoxelForgeCoreRegionConcurrentAdmission,
|
||||
GVoxelForgeCollisionGate,
|
||||
GVoxelForgeStreamingResultBacklogCap, GVoxelForgeStreamingStartupApplyBoost,
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve,
|
||||
GVoxelForgeCoreParallelForBackgroundPriority,
|
||||
GVoxelForgeStreamingTileBackgroundLow,
|
||||
GVoxelForgeStreamingTileBackgroundNormal,
|
||||
GVoxelForgeCoreColumnCacheDirectMapped,
|
||||
GVoxelForgeCoreMCGradientMemoization));
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("streaming_worker_cap_readback"), FString::Printf(
|
||||
TEXT("\"streaming_background_worker_cap\":%d,\"background_worker_threads\":%d,"
|
||||
"\"effective_tile_slots\":%d"),
|
||||
TEXT("\"streaming_background_worker_cap\":%d,\"background_worker_reserve\":%d,"
|
||||
"\"background_worker_threads\":%d,\"effective_tile_slots\":%d"),
|
||||
GVoxelForgeStreamingBackgroundWorkerCap,
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve,
|
||||
TaskGraph.GetNumBackgroundThreads(), GetMaxConcurrentStreamingTasks()));
|
||||
}
|
||||
|
||||
@@ -7626,6 +7708,7 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
"\"collision_probe_ms\":%s,\"visibility_reconcile_ms\":%s,"
|
||||
"\"visible_apply_probe_ms\":%s,\"lod0_ready_ms\":%s,"
|
||||
"\"modification_ms\":%s,\"lifecycle_audit_ms\":%s,"
|
||||
"\"active_generation_workers\":%d,\"active_tile_tasks\":%d,"
|
||||
"\"trace_json_ms\":%s,\"trace_archive_serialize_ms\":%s"),
|
||||
static_cast<unsigned long long>(GFrameCounter),
|
||||
*FString::SanitizeFloat(FPlatformTime::ToSeconds64(
|
||||
@@ -7642,6 +7725,8 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
*FString::SanitizeFloat(FPlatformTime::ToSeconds64(LOD0ReadyCycles) * 1000.0),
|
||||
*FString::SanitizeFloat(FPlatformTime::ToSeconds64(ModificationCycles) * 1000.0),
|
||||
*FString::SanitizeFloat(FPlatformTime::ToSeconds64(LifecycleAuditCycles) * 1000.0),
|
||||
ActiveGenerationWorkerCount.load(std::memory_order_relaxed),
|
||||
ActiveTileTaskCount.load(std::memory_order_relaxed),
|
||||
*FString::SanitizeFloat(TraceWriteMetrics.JsonWriteMilliseconds),
|
||||
*FString::SanitizeFloat(TraceWriteMetrics.ArchiveSerializeMilliseconds)));
|
||||
}
|
||||
@@ -11974,7 +12059,11 @@ int32 AVoxelWorld::GetMaxConcurrentStreamingTasks() const
|
||||
return TaskCap;
|
||||
}
|
||||
const int32 BackgroundWorkers = FTaskGraphInterface::Get().GetNumBackgroundThreads();
|
||||
return BackgroundWorkers > 0 ? FMath::Min(TaskCap, BackgroundWorkers) : TaskCap;
|
||||
if (BackgroundWorkers <= 0) return TaskCap;
|
||||
const int32 Reserve = FMath::Clamp(
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve, 0, FMath::Max(0, BackgroundWorkers - 1));
|
||||
const int32 AvailableWorkers = FMath::Max(1, BackgroundWorkers - Reserve);
|
||||
return FMath::Min(TaskCap, AvailableWorkers);
|
||||
}
|
||||
|
||||
int32 AVoxelWorld::GetActiveGenerationTaskCount() const
|
||||
@@ -13673,11 +13762,13 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
|
||||
ActiveTaskCount.fetch_add(1, std::memory_order_relaxed);
|
||||
ActiveTileTaskCount.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// BackgroundNormal yields to foreground work; near tasks stay ahead at BackgroundHigh.
|
||||
// BackgroundNormal cede la priorite au premier plan ; les tuiles proches restent prioritaires en BackgroundHigh.
|
||||
const UE::Tasks::ETaskPriority TaskPriority = bNearPriority
|
||||
? UE::Tasks::ETaskPriority::BackgroundHigh
|
||||
: UE::Tasks::ETaskPriority::BackgroundNormal;
|
||||
// The VR setting puts every tile task at BackgroundLow; 0 keeps near/horizon priorities.
|
||||
// Le reglage VR place chaque tuile en BackgroundLow ; 0 conserve les priorites proches/horizon.
|
||||
const UE::Tasks::ETaskPriority TaskPriority = GVoxelForgeStreamingTileBackgroundLow
|
||||
? UE::Tasks::ETaskPriority::BackgroundLow
|
||||
: (GVoxelForgeStreamingTileBackgroundNormal || !bNearPriority
|
||||
? UE::Tasks::ETaskPriority::BackgroundNormal
|
||||
: UE::Tasks::ETaskPriority::BackgroundHigh);
|
||||
UE::Tasks::Launch(TEXT("ChunkGen"), [this, Tile, OriginVoxels, Step, Cells, TaskEpoch,
|
||||
bWantCapture, bWantPreEditDensityGrid, bWantSurfaceWitnesses,
|
||||
BandVoxLo, BandVoxHi, BandChunkLo, BandChunkHi,
|
||||
@@ -13918,9 +14009,11 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
|
||||
const bool bCaptureHashes = bCaptureVoxelForgeCoreHashesForValidation;
|
||||
const bool bLegacyDirectMappedColumnCache=GVoxelForgeCoreColumnCacheDirectMapped!=0;
|
||||
const bool bMemoizeCoreMCGradients=GVoxelForgeCoreMCGradientMemoization!=0;
|
||||
const UE::Tasks::ETaskPriority TaskPriority = bNearPriority
|
||||
? UE::Tasks::ETaskPriority::BackgroundHigh
|
||||
: UE::Tasks::ETaskPriority::BackgroundNormal;
|
||||
const UE::Tasks::ETaskPriority TaskPriority = GVoxelForgeStreamingTileBackgroundLow
|
||||
? UE::Tasks::ETaskPriority::BackgroundLow
|
||||
: (GVoxelForgeStreamingTileBackgroundNormal || !bNearPriority
|
||||
? UE::Tasks::ETaskPriority::BackgroundNormal
|
||||
: UE::Tasks::ETaskPriority::BackgroundHigh);
|
||||
UE::Tasks::Launch(TEXT("VoxelForgeCoreTile"),
|
||||
[this, State, Cancellation, Tile, WorkPriority, TaskEpoch, TaskDesiredEpoch,
|
||||
TaskModificationEpoch, RequestStartCycles, RequestOrdinal, bResidentAtRequest,
|
||||
|
||||
Reference in new issue
Block a user