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:
1 parent
0a98da6fd0
commit
06f558dc91
17 files changed
+1561
-54
No files matched your search
@@ -214,6 +214,7 @@ namespace VoxelForgeCore
|
||||
double classificationExclusiveMilliseconds = 0.0;
|
||||
double marchingCubesExclusiveMilliseconds = 0.0;
|
||||
double marchingCubesDensityGridMilliseconds = 0.0;
|
||||
std::uint32_t marchingCubesGridFillWorkers = 1;
|
||||
double marchingCubesCellClassificationMilliseconds = 0.0;
|
||||
double marchingCubesGradientNormalsMilliseconds = 0.0;
|
||||
double marchingCubesEdgeInterpolationMilliseconds = 0.0;
|
||||
@@ -1462,6 +1463,7 @@ namespace VoxelForgeCore
|
||||
static void setThreadColumnInterpolationStrideSubvoxels(std::int64_t stride) noexcept;
|
||||
static std::int64_t threadColumnInterpolationStrideSubvoxels() noexcept;
|
||||
static void setThreadColumnCacheLegacyDirectMapped(bool enabled) noexcept;
|
||||
static bool threadColumnCacheLegacyDirectMapped() noexcept;
|
||||
static const HeightfieldColumnCacheStats& threadColumnCacheStats() noexcept;
|
||||
FieldInterval interval(std::int64_t minimumXSubvoxels,
|
||||
std::int64_t minimumYSubvoxels,
|
||||
@@ -1823,6 +1825,10 @@ namespace VoxelForgeCore
|
||||
// Diagnostic/performance control; memoizes exact MC lattice gradients at LOD0.
|
||||
// Controle de mesure/performance; memorise les gradients MC exacts au LOD0.
|
||||
bool memoizeMcGradients = true;
|
||||
// Optional host-owned executor for predicted-heavy density-grid fills.
|
||||
// Hosts leave this null for ordinary tiles and set a small limit for heavy tiles.
|
||||
const JobScheduler* gridFillScheduler = nullptr;
|
||||
std::uint32_t gridFillWorkerLimit = 1;
|
||||
// LOD8-only planar horizon interior grid; zero follows the MC step, 128 restores S9.
|
||||
// Grille interieure LOD8 seulement; zero suit la maille MC, 128 restaure S9.
|
||||
std::int32_t lod8HorizonInteriorStepVoxels = 0;
|
||||
@@ -1878,7 +1884,7 @@ namespace VoxelForgeCore
|
||||
bool analyticOnly = true,
|
||||
bool diagnosticIncludeBaseCoveredTiles = false);
|
||||
void retainTileRegions(std::span<const TileKey> desiredTiles);
|
||||
void buildFarWorld();
|
||||
bool buildFarWorld();
|
||||
bool isCompiled() const noexcept;
|
||||
bool hasFarWorld() const noexcept;
|
||||
ShapeBuildStats buildStats() const;
|
||||
@@ -1909,6 +1915,9 @@ namespace VoxelForgeCore
|
||||
std::vector<TileKey> desiredTiles(Vec3d cameraPositionVoxels) const;
|
||||
static std::vector<TileKey> prioritizeTiles(std::span<const TileKey> tiles,
|
||||
Vec3i centerChunk);
|
||||
// Count expanded resolved-passage route bounds intersecting the full tile bounds.
|
||||
// Hosts use this stable estimate to prioritize and split expensive near tiles.
|
||||
std::uint32_t passageOverlapScore(const TileKey& key) const noexcept;
|
||||
std::optional<TileMesh> generateTile(const TileKey& key,
|
||||
const TileGenerationOptions& options,
|
||||
std::string& error) const;
|
||||
|
||||
@@ -271,11 +271,32 @@ public:
|
||||
}
|
||||
ActivePassageTileQueries=this;
|
||||
}
|
||||
// Duplicate the immutable per-tile masks onto a density-grid helper worker. Mutable
|
||||
// segment-range caches remain lane-local so helpers never race with the parent query.
|
||||
explicit FScopedPassageTileQueries(const FScopedPassageTileQueries& source)
|
||||
:shape_(source.shape_),minimumSubvoxels_(source.minimumSubvoxels_),
|
||||
maximumSubvoxels_(source.maximumSubvoxels_),featureIndices_(source.featureIndices_),
|
||||
stampIndices_(source.stampIndices_),cavernFeatureIndices_(source.cavernFeatureIndices_),
|
||||
passageRoutes_(source.passageRoutes_),passageColumns_(source.passageColumns_),
|
||||
passageMouthSupports_(source.passageMouthSupports_),
|
||||
passageMouthSupportSegments_(source.passageMouthSupportSegments_),
|
||||
passageSegmentRangeCaches_(source.passageSegmentRangeCaches_.size()),
|
||||
upperMouthSupportPassages_(source.upperMouthSupportPassages_),
|
||||
routeSegmentCandidateCount_(source.routeSegmentCandidateCount_),
|
||||
routeFeatureOverlap_(source.routeFeatureOverlap_),columnZOverlap_(source.columnZOverlap_),
|
||||
mayContainResolvedBoundaryBand_(source.mayContainResolvedBoundaryBand_),
|
||||
mayContainTransitionPassageFade_(source.mayContainTransitionPassageFade_),
|
||||
previous_(ActivePassageTileQueries)
|
||||
{ ActivePassageTileQueries=this; }
|
||||
~FScopedPassageTileQueries(){ActivePassageTileQueries=previous_;}
|
||||
FScopedPassageTileQueries(const FScopedPassageTileQueries&)=delete;
|
||||
FScopedPassageTileQueries& operator=(const FScopedPassageTileQueries&)=delete;
|
||||
|
||||
bool matches(const FStandaloneShapeWorld& shape) const{return shape_==&shape;}
|
||||
static const FScopedPassageTileQueries* CurrentFor(const FStandaloneShapeWorld& shape)
|
||||
{
|
||||
const FScopedPassageTileQueries* current=ActivePassageTileQueries;
|
||||
return current&¤t->shape_==&shape?current:nullptr;
|
||||
}
|
||||
bool hasFeature(std::size_t passageIndex,bool columnFeature) const
|
||||
{
|
||||
const auto& candidates=columnFeature?passageColumns_:passageRoutes_;
|
||||
|
||||
@@ -1084,6 +1084,51 @@ namespace VoxelForgeCore
|
||||
bool bEnabled=false;
|
||||
};
|
||||
|
||||
struct FGridFillWorkerContext
|
||||
{
|
||||
FScopedSharedColumnCaches::FHeightfieldCache* HeightfieldCache=nullptr;
|
||||
FScopedSharedColumnCaches::FIslandCache* IslandCache=nullptr;
|
||||
const FStandaloneShapeWorld::FScopedPassageTileQueries* PassageQueries=nullptr;
|
||||
int32 Lod=0;
|
||||
bool bLegacyDirectMapped=false;
|
||||
};
|
||||
|
||||
struct FGridFillWorkerState
|
||||
{
|
||||
bool bPreviousLegacyDirectMapped=false;
|
||||
std::unique_ptr<FScopedSharedColumnCaches> SharedCaches;
|
||||
std::unique_ptr<FStandaloneShapeWorld::FScopedPassageTileQueries> PassageQueries;
|
||||
std::unique_ptr<VoxelForgeCore::Linux77Profiler::FScopedLod> Lod;
|
||||
~FGridFillWorkerState()
|
||||
{
|
||||
Lod.reset();
|
||||
PassageQueries.reset();
|
||||
SharedCaches.reset();
|
||||
Heightfield2D::setThreadColumnCacheLegacyDirectMapped(
|
||||
bPreviousLegacyDirectMapped);
|
||||
}
|
||||
};
|
||||
|
||||
void* BeginGridFillWorkerContext(void* rawContext,std::size_t)
|
||||
{
|
||||
const auto& context=*static_cast<FGridFillWorkerContext*>(rawContext);
|
||||
auto state=std::make_unique<FGridFillWorkerState>();
|
||||
state->bPreviousLegacyDirectMapped=
|
||||
Heightfield2D::threadColumnCacheLegacyDirectMapped();
|
||||
Heightfield2D::setThreadColumnCacheLegacyDirectMapped(context.bLegacyDirectMapped);
|
||||
state->SharedCaches=std::make_unique<FScopedSharedColumnCaches>(
|
||||
context.HeightfieldCache,context.IslandCache);
|
||||
if(context.PassageQueries)
|
||||
state->PassageQueries=
|
||||
std::make_unique<FStandaloneShapeWorld::FScopedPassageTileQueries>(
|
||||
*context.PassageQueries);
|
||||
state->Lod=std::make_unique<VoxelForgeCore::Linux77Profiler::FScopedLod>(context.Lod);
|
||||
return state.release();
|
||||
}
|
||||
|
||||
void EndGridFillWorkerContext(void*,void* rawState,std::size_t)
|
||||
{ delete static_cast<FGridFillWorkerState*>(rawState); }
|
||||
|
||||
World::World(ResolvedWorldSnapshot snapshot):impl_(std::make_unique<Impl>())
|
||||
{
|
||||
if(snapshot.data_)
|
||||
@@ -1737,8 +1782,13 @@ namespace VoxelForgeCore
|
||||
impl_->tileRegionCount.store(impl_->tileRegions.size(),std::memory_order_release);
|
||||
}
|
||||
|
||||
void World::buildFarWorld()
|
||||
{ if(impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape)impl_->shape->BuildFarWorld(); }
|
||||
bool World::buildFarWorld()
|
||||
{
|
||||
if(!impl_||!impl_->shapeReady.load(std::memory_order_acquire)||!impl_->shape)
|
||||
return false;
|
||||
if(!impl_->shape->HasFarWorld())impl_->shape->BuildFarWorld();
|
||||
return impl_->shape->HasFarWorld();
|
||||
}
|
||||
bool World::isCompiled() const noexcept
|
||||
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire); }
|
||||
bool World::hasFarWorld() const noexcept
|
||||
@@ -1914,6 +1964,41 @@ namespace VoxelForgeCore
|
||||
return output;
|
||||
}
|
||||
|
||||
std::uint32_t World::passageOverlapScore(const TileKey& key) const noexcept
|
||||
{
|
||||
if(!impl_||!impl_->resolvedLayout||key.lod<0||key.lod>30)return 0;
|
||||
constexpr std::int64_t SubvoxelsPerVoxel=WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr std::int64_t ExtraInfluenceVoxels=32;
|
||||
const std::int64_t Extent=static_cast<std::int64_t>(CoreTileVoxelCount)<<key.lod;
|
||||
const std::int64_t TileMinimum[3]={
|
||||
static_cast<std::int64_t>(key.coordinate.x)*Extent,
|
||||
static_cast<std::int64_t>(key.coordinate.y)*Extent,
|
||||
static_cast<std::int64_t>(key.coordinate.z)*Extent};
|
||||
const std::int64_t TileMaximum[3]={TileMinimum[0]+Extent,
|
||||
TileMinimum[1]+Extent,TileMinimum[2]+Extent};
|
||||
std::uint32_t Score=0;
|
||||
for(const auto& Passage:impl_->resolvedLayout->passages)
|
||||
for(std::size_t Index=1;Index<Passage.controlPoints.size();++Index)
|
||||
{
|
||||
const auto& A=Passage.controlPoints[Index-1];
|
||||
const auto& B=Passage.controlPoints[Index];
|
||||
const std::int64_t Radius=std::max(A.radiusSubvoxels,B.radiusSubvoxels)
|
||||
/SubvoxelsPerVoxel+ExtraInfluenceVoxels;
|
||||
bool bOverlap=true;
|
||||
for(std::size_t Axis=0;Axis<3;++Axis)
|
||||
{
|
||||
const std::int64_t AVoxel=A.positionSubvoxels[Axis]/SubvoxelsPerVoxel;
|
||||
const std::int64_t BVoxel=B.positionSubvoxels[Axis]/SubvoxelsPerVoxel;
|
||||
const std::int64_t Minimum=std::min(AVoxel,BVoxel)-Radius;
|
||||
const std::int64_t Maximum=std::max(AVoxel,BVoxel)+Radius;
|
||||
bOverlap=bOverlap&&Maximum>=TileMinimum[Axis]
|
||||
&&Minimum<=TileMaximum[Axis];
|
||||
}
|
||||
if(bOverlap)++Score;
|
||||
}
|
||||
return Score;
|
||||
}
|
||||
|
||||
std::vector<TileKey> World::desiredTiles(Vec3d cameraPosition) const
|
||||
{
|
||||
if(!impl_||!impl_->owner)return {};
|
||||
@@ -2724,6 +2809,33 @@ namespace VoxelForgeCore
|
||||
passageFeatureScope.mouthSupportSegments(passageIndex).size();
|
||||
}
|
||||
}
|
||||
FGridFillWorkerContext GridFillWorkerContext;
|
||||
if(options.gridFillScheduler&&options.gridFillWorkerLimit>1)
|
||||
{
|
||||
GridFillWorkerContext.HeightfieldCache=options.useSharedColumnCache
|
||||
?impl_->sharedHeightfieldColumns.get():nullptr;
|
||||
GridFillWorkerContext.IslandCache=options.useSharedColumnCache
|
||||
?impl_->sharedIslandColumns.get():nullptr;
|
||||
GridFillWorkerContext.PassageQueries=
|
||||
FStandaloneShapeWorld::FScopedPassageTileQueries::CurrentFor(tileShape);
|
||||
GridFillWorkerContext.Lod=key.lod;
|
||||
GridFillWorkerContext.bLegacyDirectMapped=
|
||||
options.legacyDirectMappedColumnCache||key.lod<=2;
|
||||
FVoxelGridFillExecution Execution;
|
||||
Execution.SchedulerContext=const_cast<JobScheduler*>(options.gridFillScheduler);
|
||||
Execution.ParallelFor=[](void* RawScheduler,std::size_t Count,
|
||||
std::size_t WorkerLimit,void* TaskContext,FVoxelGridFillExecution::FTask Task)
|
||||
{
|
||||
return static_cast<const JobScheduler*>(RawScheduler)->parallel_for(
|
||||
Count,1,WorkerLimit,TaskContext,Task,nullptr,0);
|
||||
};
|
||||
Execution.WorkerLimit=std::clamp<std::size_t>(
|
||||
options.gridFillWorkerLimit,2,16);
|
||||
Execution.WorkerContext=&GridFillWorkerContext;
|
||||
Execution.BeginWorkerContext=&BeginGridFillWorkerContext;
|
||||
Execution.EndWorkerContext=&EndGridFillWorkerContext;
|
||||
mesher.SetGridFillExecution(Execution);
|
||||
}
|
||||
const bool bDiagnosticColumnSampling=options.diagnosticGlobalOctaveDrop!=0
|
||||
||options.diagnosticColumnInterpolationStride>1;
|
||||
const std::uint32_t PreviousOctaveDrop=bDiagnosticColumnSampling
|
||||
@@ -2781,6 +2893,7 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.marchingCubesExclusiveMilliseconds
|
||||
-out.diagnostics.marchingCubesSkirtMilliseconds);
|
||||
out.diagnostics.marchingCubesDensityGridMilliseconds=McStageTimings.DensityGridMilliseconds;
|
||||
out.diagnostics.marchingCubesGridFillWorkers=McStageTimings.GridFillWorkerCount;
|
||||
out.diagnostics.marchingCubesCellClassificationMilliseconds=
|
||||
McStageTimings.CellClassificationMilliseconds;
|
||||
out.diagnostics.marchingCubesGradientNormalsMilliseconds=
|
||||
|
||||
@@ -2653,6 +2653,9 @@ void Heightfield2D::setThreadColumnCaptureBounds(std::int64_t minimumXSubvoxels,
|
||||
void Heightfield2D::setThreadColumnCacheLegacyDirectMapped(bool enabled) noexcept
|
||||
{ world_assembly_detail::HeightfieldColumnCacheLegacyDirectMapped=enabled; }
|
||||
|
||||
bool Heightfield2D::threadColumnCacheLegacyDirectMapped() noexcept
|
||||
{ return world_assembly_detail::HeightfieldColumnCacheLegacyDirectMapped; }
|
||||
|
||||
void Heightfield2D::setThreadColumnOctaveDrop(std::uint32_t octaveDrop) noexcept
|
||||
{ world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user