streaming: split dense passage-junction grid fills; heavy-first order + far-parent deferral in Unreal (L85)

Tiles with passage-overlap score >= 9 split grid fill over up to 4 helper lanes (disjoint rows,
same per-sample order, join before MC) when workers are idle. FI junction tile fill 173 -> 99 ms;
estimated serial floor ~0.55-0.60 s. Readiness essentially unchanged (FI/TN LOD0-2 ~2.15 s @W12):
the limit is now aggregate work, not single tiles.
Unreal: LOD0-2 ordered by passage overlap before travel/distance (L80 modes kept); far parents
built after LOD0-1, LOD2 gated on them; helper budget via ParallelFor. Unreal null-RHI FI
arrival: LOD0-2 resident at 3.65 s.
All tile hashes identical to L84 (four whole views). Fast tier PASS; Linux UE host builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-28 02:02:32 +01:00
1 parent 0a98da6fd0
commit 06f558dc91
17 files changed
+1561 -54

No files matched your search

+52 -4
View File
@@ -435,6 +435,29 @@ namespace StandaloneWorldMesher
}
}
// Grid fills can be nested inside the tile worker pool. Keep at most one such fill active so a
// burst of passage tiles adds only a bounded helper group instead of multiplying the worker count.
class FStandaloneGridFillScheduler final : public VoxelForgeCore::JobScheduler
{
public:
explicit FStandaloneGridFillScheduler(const VoxelForgeCore::JobScheduler* InInner)
:Inner(InInner){}
double parallel_for(std::size_t Count,std::size_t BatchSize,std::size_t WorkerLimit,
void* Context,Task Callback,double* WorkerActiveSeconds,
std::size_t WorkerActiveCapacity) const override
{
if(!Inner)return 0.0;
std::lock_guard<std::mutex> Lock(Mutex);
return Inner->parallel_for(Count,BatchSize,WorkerLimit,Context,Callback,
WorkerActiveSeconds,WorkerActiveCapacity);
}
private:
const VoxelForgeCore::JobScheduler* Inner=nullptr;
mutable std::mutex Mutex;
};
class FStandaloneStreamingGenerator
{
private:
@@ -462,10 +485,14 @@ public:
bool bInCaptureColumnCoordinates=false,
int32 InLod8HorizonInteriorStepVoxels=0,
bool bInSharedColumnCache=true,
bool bInAllowUniformSkipsWhenHashing=false)
bool bInAllowUniformSkipsWhenHashing=false,
const VoxelForgeCore::JobScheduler* InGridFillScheduler=nullptr)
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
CoreWorld(InCoreWorld),bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
CoreWorld(InCoreWorld),
GridFillScheduler(InGridFillScheduler
?std::make_unique<FStandaloneGridFillScheduler>(InGridFillScheduler):nullptr),
bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors),
bCaptureColumnCoordinates(bInCaptureColumnCoordinates),
bSharedColumnCache(bInSharedColumnCache),
@@ -1116,6 +1143,25 @@ private:
Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors;
Options.captureColumnCoordinates=bCaptureColumnCoordinates;
Options.useSharedColumnCache=bSharedColumnCache;
// Split only dense passage/mouth overlaps, and spend only worker lanes left idle by
// the tile pool. The grid fill runs on a second pool while this tile worker waits.
const uint32 PassageScore=PassageOverlapScore(Job.Key);
if(GridFillScheduler&&Job.Key.Level==0&&WorkerCount>1&&PassageScore>=9)
{
size_t ActiveTileCount=0;
{
std::lock_guard<std::mutex> Lock(Mutex);
ActiveTileCount=Running.size();
}
const int32 AvailableHelperLanes=std::max(0,
12-std::max(0,static_cast<int32>(ActiveTileCount)-1));
const int32 GridFillWorkers=std::min({4,WorkerCount,AvailableHelperLanes});
if(GridFillWorkers>1)
{
Options.gridFillScheduler=GridFillScheduler.get();
Options.gridFillWorkerLimit=static_cast<std::uint32_t>(GridFillWorkers);
}
}
Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels;
Options.profileMesherStages=(bProfileMesherStages||bInstrumentTileDrain)&&!ForceGenerate;
Options.profilePassageComposer=bProfilePassageComposer;
@@ -1330,7 +1376,7 @@ private:
const char* TileClassName=D.tileClass==VoxelForgeCore::TileClass::allAir?"air":
D.tileClass==VoxelForgeCore::TileClass::allSolid?"solid":
D.tileClass==VoxelForgeCore::TileClass::mixed?"mixed":"unknown";
std::printf("[CpuTileStages] key=(%d,%d,%d,lod%d) worker_id=%d worker_ms=%.3f payload_ms=%.3f tile_classified=%d tile_class=%s tile_skip=%d density_point_calls=%llu density_float_points=%llu density_lattice_points=%llu density_unclamped_points=%llu density_x8_points=%llu field_eval_sample_ns=%llu field_eval_sample_points=%llu samples=%llu classify_ms=%.3f column_ms=%.3f column_exclusive_ms=%.3f mc_ms=%.3f mc_grid_ms=%.3f mc_cell_ms=%.3f mc_gradient_ms=%.3f mc_interpolation_ms=%.3f mc_dedup_ms=%.3f mc_skirt_ms=%.3f mc_other_ms=%.3f connector_ms=%.3f ground_ms=%.3f cap_ms=%.3f horizon_ms=%.3f mesh_assembly_ms=%.3f candidate_cells=%llu classified_cells=%llu surface_cells=%llu proof_solid_cells=%llu proof_air_cells=%llu proof_density_samples=%llu zband_reject_cells=%llu x8_calls=%llu x8_active_lanes=%llu grad_calls=%llu grad_memo_hits=%llu grad_fallback_calls=%llu midpoint_attempts=%llu midpoint_memo_hits=%llu skirt_edges=%llu skirt_boundary_edges=%llu skirt_density_probes=%llu skirt_triangles=%u fi_local_hit=%llu fi_local_miss=%llu fi_local_conflicts=%llu fi_shared_hit=%llu fi_shared_miss=%llu fi_shared_computes=%llu fi_shared_conflicts=%llu fi_lock_wait_us=%.3f\n",
std::printf("[CpuTileStages] key=(%d,%d,%d,lod%d) worker_id=%d worker_ms=%.3f payload_ms=%.3f tile_classified=%d tile_class=%s tile_skip=%d density_point_calls=%llu density_float_points=%llu density_lattice_points=%llu density_unclamped_points=%llu density_x8_points=%llu field_eval_sample_ns=%llu field_eval_sample_points=%llu samples=%llu classify_ms=%.3f column_ms=%.3f column_exclusive_ms=%.3f mc_ms=%.3f mc_grid_ms=%.3f mc_grid_workers=%u mc_cell_ms=%.3f mc_gradient_ms=%.3f mc_interpolation_ms=%.3f mc_dedup_ms=%.3f mc_skirt_ms=%.3f mc_other_ms=%.3f connector_ms=%.3f ground_ms=%.3f cap_ms=%.3f horizon_ms=%.3f mesh_assembly_ms=%.3f candidate_cells=%llu classified_cells=%llu surface_cells=%llu proof_solid_cells=%llu proof_air_cells=%llu proof_density_samples=%llu zband_reject_cells=%llu x8_calls=%llu x8_active_lanes=%llu grad_calls=%llu grad_memo_hits=%llu grad_fallback_calls=%llu midpoint_attempts=%llu midpoint_memo_hits=%llu skirt_edges=%llu skirt_boundary_edges=%llu skirt_density_probes=%llu skirt_triangles=%u fi_local_hit=%llu fi_local_miss=%llu fi_local_conflicts=%llu fi_shared_hit=%llu fi_shared_miss=%llu fi_shared_computes=%llu fi_shared_conflicts=%llu fi_lock_wait_us=%.3f\n",
Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level,
Event.WorkerId,Event.WorkerMilliseconds,Event.PayloadBuildMilliseconds,
D.tileWasClassified?1:0,TileClassName,D.meshingSkipped?1:0,
@@ -1344,7 +1390,8 @@ private:
static_cast<unsigned long long>(Event.DensitySamples),D.classificationMilliseconds,
D.columnComputeMilliseconds,D.columnEvaluationExclusiveMilliseconds,
D.marchingCubesExclusiveMilliseconds+D.marchingCubesSkirtMilliseconds,
D.marchingCubesDensityGridMilliseconds,D.marchingCubesCellClassificationMilliseconds,
D.marchingCubesDensityGridMilliseconds,D.marchingCubesGridFillWorkers,
D.marchingCubesCellClassificationMilliseconds,
D.marchingCubesGradientNormalsMilliseconds,D.marchingCubesEdgeInterpolationMilliseconds,
D.marchingCubesVertexDedupStreamMilliseconds,D.marchingCubesSkirtMilliseconds,
D.marchingCubesOtherMilliseconds,D.farConnectorShellMilliseconds,
@@ -1441,6 +1488,7 @@ private:
const FStandaloneMeshCache* MeshCache=nullptr;
int32 MaxLevelFilter=-1;
VoxelForgeCore::World* CoreWorld=nullptr;
std::unique_ptr<FStandaloneGridFillScheduler> GridFillScheduler;
bool bSkipUvAndColors=false;
bool bSkipMaterialGroups=false;
bool bSkipUV=false;