wip(assembler): round S5 -- streaming gates + trace; coarse-first order kept opt-in (no win)
Walkable/enclosed/refined gates defined; JSONL trace joins request, worker start, build/apply and discards. Coarse-first + dynamic near reserve behind voxel.StreamingCoarseFirst / voxel.StreamingDynamicNearReserve (default 0 = S4 order): matched runs showed no walkability or full-view win and slower LOD0/1 refinement. Instruments settled: 3.1 s standalone reproduced (16.7 s was another generation route); black early captures were clear-only frames. Hashes equal S4; Core-off, M2, SurfaceWorld/FloatingIslands BuildHost pass. 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
9d3c50ca95
commit
c0596c5f24
32 files changed
+1049
-65
No files matched your search
@@ -9,6 +9,7 @@
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
@@ -409,6 +410,11 @@ namespace StandaloneWorldMesher
|
||||
class FStandaloneStreamingGenerator
|
||||
{
|
||||
public:
|
||||
static void SetCoarseFirstSchedulingEnabled(bool bEnabled)
|
||||
{bCoarseFirstSchedulingEnabled.store(bEnabled,std::memory_order_relaxed);}
|
||||
static bool IsCoarseFirstSchedulingEnabled()
|
||||
{return bCoarseFirstSchedulingEnabled.load(std::memory_order_relaxed);}
|
||||
|
||||
explicit FStandaloneStreamingGenerator(FStandaloneOwnerWorld& InWorld,int32 InWorkerCount=8,
|
||||
bool bInHashDensity=false,
|
||||
const FStandaloneMeshCache* InMeshCache=nullptr,
|
||||
@@ -448,6 +454,7 @@ public:
|
||||
bStop=false;
|
||||
}
|
||||
for(int32 I=0;I<WorkerCount;++I)Workers.emplace_back([this](){WorkerLoop();});
|
||||
bHasLastCameraPosition=false;TravelDirection=FVector::ZeroVector;
|
||||
return UpdateDesired(CameraPositionVoxels,ResidentKeys,CrossingIndex);
|
||||
}
|
||||
|
||||
@@ -461,6 +468,7 @@ public:
|
||||
bStop=false;
|
||||
}
|
||||
for(int32 I=0;I<WorkerCount;++I)Workers.emplace_back([this](){WorkerLoop();});
|
||||
bHasLastCameraPosition=false;TravelDirection=FVector::ZeroVector;
|
||||
return UpdateDesiredSet(InitialDesired,ResidentKeys,CrossingIndex);
|
||||
}
|
||||
|
||||
@@ -507,6 +515,23 @@ public:
|
||||
const FVector& CameraPositionVoxels,const std::vector<FVoxelTileKey>& ResidentKeys,
|
||||
uint64 CrossingIndex=0)
|
||||
{
|
||||
if(bHasLastCameraPosition)
|
||||
{
|
||||
const FVector Delta=CameraPositionVoxels-LastCameraPositionVoxels;
|
||||
const double LengthSquared=static_cast<double>(Delta.X)*Delta.X
|
||||
+static_cast<double>(Delta.Y)*Delta.Y;
|
||||
if(LengthSquared>1.0e-12)
|
||||
{
|
||||
const double InverseLength=1.0/std::sqrt(LengthSquared);
|
||||
TravelDirection=FVector(static_cast<float>(Delta.X*InverseLength),
|
||||
static_cast<float>(Delta.Y*InverseLength),0.0f);
|
||||
}
|
||||
else TravelDirection=FVector::ZeroVector;
|
||||
}
|
||||
else
|
||||
TravelDirection=FVector::ZeroVector;
|
||||
LastCameraPositionVoxels=CameraPositionVoxels;
|
||||
bHasLastCameraPosition=true;
|
||||
FStandaloneDesiredTileSet NewDesired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraPositionVoxels);
|
||||
if(CoreWorld)
|
||||
{
|
||||
@@ -652,17 +677,60 @@ private:
|
||||
Stats.CancelledByCrossing.reserve(CancelledByCrossing.size());
|
||||
for(const auto& Pair:CancelledByCrossing)Stats.CancelledByCrossing.push_back(Pair);
|
||||
|
||||
std::vector<VoxelForgeCore::TileKey> Unordered;Unordered.reserve(NewDesired.Tiles.size());
|
||||
for(const FVoxelTileKey& Key:NewDesired.Tiles)
|
||||
Unordered.push_back({{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level});
|
||||
const auto Prioritized=VoxelForgeCore::World::prioritizeTiles(Unordered,
|
||||
{NewDesired.CenterChunk.X,NewDesired.CenterChunk.Y,NewDesired.CenterChunk.Z});
|
||||
std::vector<FVoxelTileKey> Prioritized=NewDesired.Tiles;
|
||||
if(IsCoarseFirstSchedulingEnabled())
|
||||
{
|
||||
// The local walkable ring leads, the coarse enclosure follows, then target-detail work.
|
||||
// L'anneau praticable local passe d'abord, puis l'enveloppe grossiere, puis le detail cible.
|
||||
const int32 FirstCoarseLevel=FMath::Max(2,World->FullResClipLevels);
|
||||
auto Tier=[&](const FVoxelTileKey& Key)
|
||||
{
|
||||
if(Key.Level==0&&std::abs(Key.Coord.X-NewDesired.CenterChunk.X)<=1
|
||||
&&std::abs(Key.Coord.Y-NewDesired.CenterChunk.Y)<=1
|
||||
&&std::abs(Key.Coord.Z-NewDesired.CenterChunk.Z)<=1)return 0;
|
||||
return Key.Level>=FirstCoarseLevel?1:2;
|
||||
};
|
||||
auto IsAhead=[&](const FVoxelTileKey& Key)
|
||||
{
|
||||
if(TravelDirection.X*TravelDirection.X+TravelDirection.Y*TravelDirection.Y<=1.0e-12f)
|
||||
return false;
|
||||
const double Scale=static_cast<double>(1<<Key.Level);
|
||||
const double OffsetX=(static_cast<double>(Key.Coord.X)+0.5)*Scale-NewDesired.CenterChunk.X;
|
||||
const double OffsetY=(static_cast<double>(Key.Coord.Y)+0.5)*Scale-NewDesired.CenterChunk.Y;
|
||||
return OffsetX*TravelDirection.X+OffsetY*TravelDirection.Y>0.0;
|
||||
};
|
||||
std::sort(Prioritized.begin(),Prioritized.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
|
||||
{
|
||||
const int32 TierA=Tier(A),TierB=Tier(B);
|
||||
if(TierA!=TierB)return TierA<TierB;
|
||||
if(TierA==1&&A.Level!=B.Level)return A.Level>B.Level;
|
||||
const bool bAheadA=IsAhead(A),bAheadB=IsAhead(B);
|
||||
if(bAheadA!=bAheadB)return bAheadA;
|
||||
const double DA=StandaloneWorldMesher::TileDistanceSquaredChunks(A,NewDesired.CenterChunk);
|
||||
const double DB=StandaloneWorldMesher::TileDistanceSquaredChunks(B,NewDesired.CenterChunk);
|
||||
if(DA!=DB)return DA<DB;
|
||||
if(A.Level!=B.Level)return A.Level<B.Level;
|
||||
if(A.Coord.Z!=B.Coord.Z)return A.Coord.Z<B.Coord.Z;
|
||||
if(A.Coord.Y!=B.Coord.Y)return A.Coord.Y<B.Coord.Y;
|
||||
return A.Coord.X<B.Coord.X;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<VoxelForgeCore::TileKey> Unordered;Unordered.reserve(NewDesired.Tiles.size());
|
||||
for(const FVoxelTileKey& Key:NewDesired.Tiles)
|
||||
Unordered.push_back({{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level});
|
||||
const auto CorePrioritized=VoxelForgeCore::World::prioritizeTiles(Unordered,
|
||||
{NewDesired.CenterChunk.X,NewDesired.CenterChunk.Y,NewDesired.CenterChunk.Z});
|
||||
Prioritized.clear();Prioritized.reserve(CorePrioritized.size());
|
||||
for(const auto& Key:CorePrioritized)
|
||||
Prioritized.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
}
|
||||
std::unordered_map<FVoxelTileKey,size_t,FStandaloneTileKeyHash> PriorityRanks;
|
||||
PriorityRanks.reserve(Prioritized.size()*2+1);
|
||||
for(size_t Rank=0;Rank<Prioritized.size();++Rank)
|
||||
{
|
||||
const auto& Key=Prioritized[Rank];
|
||||
PriorityRanks.emplace(FVoxelTileKey(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod),Rank);
|
||||
PriorityRanks.emplace(Prioritized[Rank],Rank);
|
||||
}
|
||||
for(FStandaloneTileJob& Job:Queue)Job.PriorityRank=PriorityRanks.at(Job.Key);
|
||||
|
||||
@@ -884,6 +952,10 @@ private:
|
||||
TimelineMilliseconds(Started),TimelineMilliseconds(Finished),WorkerMs);
|
||||
}
|
||||
FStandaloneOwnerWorld* World=nullptr;
|
||||
inline static std::atomic<bool> bCoarseFirstSchedulingEnabled{true};
|
||||
FVector LastCameraPositionVoxels=FVector::ZeroVector;
|
||||
FVector TravelDirection=FVector::ZeroVector;
|
||||
bool bHasLastCameraPosition=false;
|
||||
int32 WorkerCount=8;
|
||||
bool bHashDensity=false;
|
||||
const FStandaloneMeshCache* MeshCache=nullptr;
|
||||
|
||||
Reference in new issue
Block a user