#pragma once #include "WorldMesher.h" #include "StandaloneWorkerThread.h" #include "VoxelForgeCore/VoxelForgeCore.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct FStandaloneTileKeyHash { std::size_t operator()(const FVoxelTileKey& Key) const noexcept { return static_cast(GetTypeHash(Key)); } }; struct FStandaloneDesiredTileSet { std::vector Tiles; FIntVector CenterChunk=FIntVector::ZeroValue; int32 MinimumChunkZ=MIN_int32; int32 MaximumChunkZ=MAX_int32; int32 StrateTopChunkZ=0; int32 StrateBottomChunkZ=0; bool bHasStrate=false; }; struct FStandaloneCpuTile { FVoxelTileKey Key; int32 WorkerId=-1; uint64 RequestedCrossing=0; std::vector Vertices; std::vector Indices; std::vector TriangleSkirtFaceMasks; uint32 SurfaceVertexCount=0; uint32 SkirtTriangleCount=0; uint64 DensitySamples=0; double WorkerSeconds=0.0; std::chrono::steady_clock::time_point QueuedAt{}; std::chrono::steady_clock::time_point StartedAt{}; std::chrono::steady_clock::time_point CompletedAt{}; uint64 DensityHash=14695981039346656037ull; bool bHasDensityHash=false; uint64 MeshHash=14695981039346656037ull; bool bHasMeshHash=false; uint64 PayloadHash=14695981039346656037ull; bool bHasPayloadHash=false; uint64 TopologyHash=14695981039346656037ull; bool bHasTopologyHash=false; uint64 MaterialHash=14695981039346656037ull; bool bHasMaterialHash=false; bool bGenerationFailed=false; uint64 MeshPayloadAllocatedBytes=0; VoxelForgeCore::TileGenerationStats CoreDiagnostics{}; std::array ProfileCycles{}; }; inline uint64 HashStandaloneMeshPayload(const FVoxelMeshData& Mesh) { uint64 Hash=14695981039346656037ull; const auto AddWord=[&](uint64 Word,int32 Bytes) { for(int32 I=0;I(Word>>(I*8)); Hash*=1099511628211ull; } }; const auto AddDouble=[&](double Value) { AddWord(std::bit_cast(Value),8); }; AddWord(static_cast(Mesh.Vertices.Num()),8); for(const FVector& Position:Mesh.Vertices) { AddDouble(Position.X);AddDouble(Position.Y);AddDouble(Position.Z); } AddWord(static_cast(Mesh.Normals.Num()),8); for(const FVector& Normal:Mesh.Normals) { AddDouble(Normal.X);AddDouble(Normal.Y);AddDouble(Normal.Z); } AddWord(static_cast(Mesh.UVs.Num()),8); for(const auto& UV:Mesh.UVs) { AddDouble(static_cast(UV.X));AddDouble(static_cast(UV.Y)); } AddWord(static_cast(Mesh.Colors.Num()),8); for(const FColor& Color:Mesh.Colors) { AddWord(Color.R,1);AddWord(Color.G,1);AddWord(Color.B,1);AddWord(Color.A,1); } AddWord(static_cast(Mesh.Triangles.Num()),8); for(const int32 Index:Mesh.Triangles)AddWord(static_cast(Index),4); AddWord(static_cast(Mesh.TriangleSkirtFaceMasks.Num()),8); for(const uint8 Faces:Mesh.TriangleSkirtFaceMasks)AddWord(Faces,1); AddWord(static_cast(Mesh.NumSurfaceVertices),4); AddWord(static_cast(Mesh.NumSkirtTriangles),4); AddWord(static_cast(Mesh.NumCeilingTriangles),4); return Hash; } inline uint64 HashStandaloneMeshTopology(const FVoxelMeshData& Mesh) { uint64 Hash=14695981039346656037ull; const auto AddWord=[&](uint64 Word,int32 Bytes) { for(int32 I=0;I(Word>>(I*8)); Hash*=1099511628211ull; } }; using FTriangleKey=std::array; const auto VertexKey=[&](int32 Index) { uint64 VertexHash=14695981039346656037ull; const auto AddVertexWord=[&](uint64 Word) { for(int32 Byte=0;Byte<8;++Byte) { VertexHash^=static_cast(Word>>(Byte*8)); VertexHash*=1099511628211ull; } }; if(Index<0||Index>=Mesh.Vertices.Num()||Index>=Mesh.Normals.Num()) { AddVertexWord(UINT64_MAX);return VertexHash; } const FVector& Position=Mesh.Vertices[Index]; const FVector& Normal=Mesh.Normals[Index]; AddVertexWord(std::bit_cast(static_cast(Position.X))); AddVertexWord(std::bit_cast(static_cast(Position.Y))); AddVertexWord(std::bit_cast(static_cast(Position.Z))); AddVertexWord(std::bit_cast(static_cast(Normal.X))); AddVertexWord(std::bit_cast(static_cast(Normal.Y))); AddVertexWord(std::bit_cast(static_cast(Normal.Z))); return VertexHash; }; std::vector Triangles; Triangles.reserve(static_cast(Mesh.Triangles.Num()/3)); for(int32 I=0;I+2(Triangles.size()),8); for(const FTriangleKey& Triangle:Triangles) for(const uint64 VertexHash:Triangle)AddWord(VertexHash,8); return Hash; } inline uint64 HashStandaloneCpuTileMesh(const FStandaloneCpuTile& Tile) { uint64 Hash=14695981039346656037ull; const auto AddWord=[&](uint64 Word,int32 Bytes) { for(int32 I=0;I(Word>>(I*8)); Hash*=1099511628211ull; } }; AddWord(Tile.Vertices.size(),8); for(const FStandaloneCpuVertex& Vertex:Tile.Vertices) { for(const float Value:Vertex.Position)AddWord(std::bit_cast(Value),4); for(const float Value:Vertex.Normal)AddWord(std::bit_cast(Value),4); } AddWord(Tile.Indices.size(),8); for(const uint32 Index:Tile.Indices)AddWord(Index,4); return Hash; } // Far parent repairs and shell stitching are implemented by VoxelForgeCore. struct FStandaloneMeshCacheMetadata { uint64 SourceDataHash=0; int32 Seed=0; int32 ClipRadius=3; int32 FullResClipLevels=2; int32 CoarseTileCells=16; int32 StrateContentCutMinLevel=9; int32 SkirtCells=0; int32 LODOctaveDrop=0; int32 bGenerateSkirts=0; uint32 AlgorithmVersion=1; uint32 TunnelMode=0; uint32 VoxelSizeCm=25; }; class FStandaloneMeshCache { public: static constexpr uint32 CurrentVersion=1; void Clear(){Tiles.clear();bLoaded=false;BytesLoaded=0;} bool IsLoaded()const{return bLoaded;} size_t Num()const{return Tiles.size();} uint64 GetBytesLoaded()const{return BytesLoaded;} const FStandaloneCpuTile* Find(const FVoxelTileKey& Key)const { const auto It=Tiles.find(Key); return It==Tiles.end()?nullptr:&It->second; } static bool Save(const std::string& Path,const FStandaloneMeshCacheMetadata& Metadata, std::vector& InTiles,std::string& Error) { std::vector Ordered; Ordered.reserve(InTiles.size()); for(FStandaloneCpuTile& Tile:InTiles)Ordered.push_back(&Tile); std::sort(Ordered.begin(),Ordered.end(),[](const FStandaloneCpuTile* A,const FStandaloneCpuTile* B) { if(A->Key.Level!=B->Key.Level)return A->Key.LevelKey.Level; if(A->Key.Coord.Z!=B->Key.Coord.Z)return A->Key.Coord.ZKey.Coord.Z; if(A->Key.Coord.Y!=B->Key.Coord.Y)return A->Key.Coord.YKey.Coord.Y; return A->Key.Coord.XKey.Coord.X; }); std::ofstream File(Path,std::ios::binary|std::ios::trunc); if(!File){Error="could not open mesh cache for writing: "+Path;return false;} struct FHeader { char Magic[8];uint32 Version;uint32 Endian;uint64 SourceDataHash; int32 Seed,ClipRadius,FullResClipLevels,CoarseTileCells,StrateContentCutMinLevel; int32 SkirtCells,LODOctaveDrop,bGenerateSkirts; uint32 AlgorithmVersion,TunnelMode,VoxelSizeCm; uint64 TileCount; }; static_assert(sizeof(FHeader)==80); const FHeader Header{{'V','F','M','E','S','H','1','\0'},1,0x01020304u, Metadata.SourceDataHash,Metadata.Seed,Metadata.ClipRadius,Metadata.FullResClipLevels, Metadata.CoarseTileCells,Metadata.StrateContentCutMinLevel,Metadata.SkirtCells, Metadata.LODOctaveDrop,Metadata.bGenerateSkirts,Metadata.AlgorithmVersion, Metadata.TunnelMode,Metadata.VoxelSizeCm,static_cast(Ordered.size())}; File.write(reinterpret_cast(&Header),sizeof(Header)); struct FRecord { int32 Level,X,Y,Z;uint64 Samples,DensityHash;uint32 VertexCount,IndexCount; }; static_assert(sizeof(FRecord)==40); for(const FStandaloneCpuTile* Tile:Ordered) { if(Tile->Vertices.size()>UINT32_MAX||Tile->Indices.size()>UINT32_MAX ||Tile->Indices.size()%3!=0) {Error="mesh cache tile exceeds the 32-bit record limits";return false;} const FRecord Record{Tile->Key.Level,Tile->Key.Coord.X,Tile->Key.Coord.Y, Tile->Key.Coord.Z,Tile->DensitySamples,Tile->DensityHash, static_cast(Tile->Vertices.size()),static_cast(Tile->Indices.size())}; File.write(reinterpret_cast(&Record),sizeof(Record)); if(!Tile->Vertices.empty())File.write(reinterpret_cast(Tile->Vertices.data()), static_cast(Tile->Vertices.size()*sizeof(FStandaloneCpuVertex))); if(!Tile->Indices.empty())File.write(reinterpret_cast(Tile->Indices.data()), static_cast(Tile->Indices.size()*sizeof(uint32))); if(!File){Error="mesh cache write failed: "+Path;return false;} } File.flush(); if(!File){Error="mesh cache flush failed: "+Path;return false;} return true; } bool Load(const std::string& Path,const FStandaloneMeshCacheMetadata& Expected,std::string& Error) { Clear(); std::ifstream File(Path,std::ios::binary); if(!File){Error="cache file is missing or unreadable";return false;} struct FHeader { char Magic[8];uint32 Version;uint32 Endian;uint64 SourceDataHash; int32 Seed,ClipRadius,FullResClipLevels,CoarseTileCells,StrateContentCutMinLevel; int32 SkirtCells,LODOctaveDrop,bGenerateSkirts; uint32 AlgorithmVersion,TunnelMode,VoxelSizeCm; uint64 TileCount; }; static_assert(sizeof(FHeader)==80); FHeader H{};File.read(reinterpret_cast(&H),sizeof(H)); const char ExpectedMagic[8]={'V','F','M','E','S','H','1','\0'}; if(!File||std::memcmp(H.Magic,ExpectedMagic,sizeof(ExpectedMagic))!=0 ||H.Version!=1||H.Endian!=0x01020304u||H.TileCount>1000000) {Error="mesh cache header is invalid";return false;} if(H.SourceDataHash!=Expected.SourceDataHash||H.Seed!=Expected.Seed ||H.ClipRadius!=Expected.ClipRadius||H.FullResClipLevels!=Expected.FullResClipLevels ||H.CoarseTileCells!=Expected.CoarseTileCells ||H.StrateContentCutMinLevel!=Expected.StrateContentCutMinLevel ||H.SkirtCells!=Expected.SkirtCells||H.LODOctaveDrop!=Expected.LODOctaveDrop ||H.bGenerateSkirts!=Expected.bGenerateSkirts ||H.AlgorithmVersion!=Expected.AlgorithmVersion||H.TunnelMode!=Expected.TunnelMode ||H.VoxelSizeCm!=Expected.VoxelSizeCm) {Error="mesh cache metadata does not match this world configuration";return false;} struct FRecord { int32 Level,X,Y,Z;uint64 Samples,DensityHash;uint32 VertexCount,IndexCount; }; static_assert(sizeof(FRecord)==40); std::unordered_map Loaded; Loaded.reserve(static_cast(H.TileCount)*2+1); for(uint64 I=0;I(&Record),sizeof(Record)); if(!File||Record.Level<0||Record.Level>8||Record.VertexCount>10000000 ||Record.IndexCount>60000000) {Error="mesh cache contains an invalid tile record";return false;} FStandaloneCpuTile Tile;Tile.Key=FVoxelTileKey(FIntVector(Record.X,Record.Y,Record.Z),Record.Level); Tile.DensitySamples=Record.Samples;Tile.DensityHash=Record.DensityHash;Tile.bHasDensityHash=true; Tile.Vertices.resize(Record.VertexCount);Tile.Indices.resize(Record.IndexCount); if(!Tile.Vertices.empty())File.read(reinterpret_cast(Tile.Vertices.data()), static_cast(Tile.Vertices.size()*sizeof(FStandaloneCpuVertex))); if(!Tile.Indices.empty())File.read(reinterpret_cast(Tile.Indices.data()), static_cast(Tile.Indices.size()*sizeof(uint32))); if(!File){Error="mesh cache ended inside tile geometry";return false;} for(const uint32 Index:Tile.Indices) if(Index>=Tile.Vertices.size()) {Error="mesh cache contains an out-of-range mesh index";return false;} for(const FStandaloneCpuVertex& Vertex:Tile.Vertices) for(const float Value:Vertex.Position) if(!std::isfinite(Value)) {Error="mesh cache contains a non-finite vertex";return false;} for(const FStandaloneCpuVertex& Vertex:Tile.Vertices) for(const float Value:Vertex.Normal) if(!std::isfinite(Value)) {Error="mesh cache contains a non-finite normal";return false;} if(!Loaded.emplace(Tile.Key,std::move(Tile)).second) {Error="mesh cache contains duplicate tile keys";return false;} } const std::streamoff End=File.tellg(); if(End<0){Error="could not determine mesh cache size";return false;} BytesLoaded=static_cast(End);Tiles.swap(Loaded);bLoaded=true; return true; } private: std::unordered_map Tiles; bool bLoaded=false; uint64 BytesLoaded=0; }; struct FStandaloneStreamingUpdate { FIntVector CenterChunk=FIntVector::ZeroValue; int32 DesiredTiles=0; int32 RetainedResidentTiles=0; int32 NewJobs=0; int32 CancelledQueuedJobs=0; int32 FullResTiles=0; std::vector> CancelledByCrossing; }; struct FStandaloneTileJob { FVoxelTileKey Key; int32 BandZMin=MIN_int32; int32 BandZMax=MAX_int32; uint64 RequestedCrossing=0; size_t PriorityRank=0; std::chrono::steady_clock::time_point QueuedAt{}; std::chrono::steady_clock::time_point StartedAt{}; }; struct FStandaloneCpuTileTimelineEvent { enum class EType : uint8 { Queued, Dispatch, Finish }; EType Type=EType::Queued; FVoxelTileKey Key; int32 WorkerId=-1; size_t QueueDepth=0; size_t ActiveWorkers=0; size_t PriorityRank=0; std::chrono::steady_clock::time_point At{}; std::chrono::steady_clock::time_point QueuedAt{}; std::chrono::steady_clock::time_point StartedAt{}; std::chrono::steady_clock::time_point FinishedAt{}; double WorkerMilliseconds=-1.0; uint64 DensitySamples=0; uint32 SkirtTriangles=0; VoxelForgeCore::TileGenerationStats Diagnostics{}; }; struct FStandaloneTileJobHeapOrder { bool operator()(const FStandaloneTileJob& A,const FStandaloneTileJob& B)const { if(A.PriorityRank!=B.PriorityRank)return A.PriorityRank>B.PriorityRank; if(A.Key.Level!=B.Key.Level)return A.Key.Level>B.Key.Level; if(A.Key.Coord.Z!=B.Key.Coord.Z)return A.Key.Coord.Z>B.Key.Coord.Z; if(A.Key.Coord.Y!=B.Key.Coord.Y)return A.Key.Coord.Y>B.Key.Coord.Y; return A.Key.Coord.X>B.Key.Coord.X; } }; namespace StandaloneWorldMesher { inline double TileDistanceSquaredChunks(const FVoxelTileKey& Tile,const FIntVector& Center) { const double Scale=static_cast(1<(Tile.Coord.X)+0.5)*Scale-Center.X; const double DY=(static_cast(Tile.Coord.Y)+0.5)*Scale-Center.Y; const double DZ=(static_cast(Tile.Coord.Z)+0.5)*Scale-Center.Z; return DX*DX+DY*DY+DZ*DZ; } inline FStandaloneDesiredTileSet BuildDesiredSet( const FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels) { FStandaloneDesiredTileSet Desired; Desired.CenterChunk=CameraChunk(CameraPositionVoxels); if(World.Settings->bClampViewToStrate) Desired.bHasStrate=World.Manager->GetStrateChunkZBounds( Desired.CenterChunk.Z,Desired.StrateTopChunkZ,Desired.StrateBottomChunkZ); VoxelClipmapDesiredTiles::ResolveVerticalBounds( Desired.CenterChunk.Z,World.Settings->ViewDistanceUp,World.Settings->ViewDistanceDown, World.Settings->StrateViewMarginChunks,World.Settings->bClampViewToStrate, Desired.bHasStrate,Desired.StrateTopChunkZ,Desired.StrateBottomChunkZ, Desired.MinimumChunkZ,Desired.MaximumChunkZ); VoxelClipmapDesiredTiles::FParameters Parameters; Parameters.ClipRadius=World.ClipRadius; Parameters.MaxClipLevel=FMath::Clamp(World.MaxClipLevel,0,8); Parameters.RenderDistanceChunks=World.RenderDistanceChunks; TArray Tiles; VoxelClipmapDesiredTiles::Build(Desired.CenterChunk,Desired.MinimumChunkZ, Desired.MaximumChunkZ,Parameters,Tiles); Desired.Tiles.reserve(static_cast(Tiles.Num())); for(const FVoxelTileKey& Tile:Tiles)Desired.Tiles.push_back(Tile); std::sort(Desired.Tiles.begin(),Desired.Tiles.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B) { const double DA=TileDistanceSquaredChunks(A,Desired.CenterChunk); const double DB=TileDistanceSquaredChunks(B,Desired.CenterChunk); return DA==DB?A.Level* FrameOrdinal) { TimelineOrigin=Origin; TimelineFrameOrdinal=FrameOrdinal; TimelineQueueEvents.clear(); TimelineWorkerEvents.clear(); TimelineWorkerEvents.resize(static_cast(WorkerCount)); bColdTimelineTrace=true; } void DumpColdTimelineTrace() const { if(!bColdTimelineTrace)return; std::vector Events=TimelineQueueEvents; size_t Total=Events.size(); for(const auto& WorkerEvents:TimelineWorkerEvents)Total+=WorkerEvents.size(); Events.reserve(Total); for(const auto& WorkerEvents:TimelineWorkerEvents) Events.insert(Events.end(),WorkerEvents.begin(),WorkerEvents.end()); std::stable_sort(Events.begin(),Events.end(),[](const auto& A,const auto& B) {return A.At(TimelineFrame()), Event.WorkerId,Event.QueueDepth,Event.ActiveWorkers,Event.PriorityRank, TimelineMilliseconds(Event.QueuedAt),TimelineMilliseconds(Event.StartedAt), TimelineMilliseconds(Event.FinishedAt), Event.StartedAt.time_since_epoch().count()!=0 ?std::chrono::duration(Event.At-Event.StartedAt).count():-1.0, Event.WorkerMilliseconds); if(Event.Type==FStandaloneCpuTileTimelineEvent::EType::Finish) PrintTileStageEvent(Event); } std::fflush(stdout); } FStandaloneStreamingUpdate Start(const FVector& CameraPositionVoxels, const std::vector& ResidentKeys={}, uint64 CrossingIndex=0) { Stop(); { std::lock_guard Lock(Mutex); bStop=false; } for(int32 I=0;I& ResidentKeys={}, uint64 CrossingIndex=0) { Stop(); { std::lock_guard Lock(Mutex); bStop=false; } for(int32 I=0;I& Keys) { TileFilter.clear();TileFilter.reserve(Keys.size()*2+1); for(const FVoxelTileKey& Key:Keys)TileFilter.insert(Key); bUseTileFilter=true; } bool QueuePriorityTasks(std::vector> Tasks) { if(Tasks.empty())return true; { std::lock_guard Lock(Mutex); if(bStop||Workers.empty())return false; for(auto& Task:Tasks)PriorityTasks.push_back(std::move(Task)); } Wake.notify_all(); return true; } void ClearPriorityTasks() { std::lock_guard Lock(Mutex); PriorityTasks.clear(); } void Stop() { { std::lock_guard Lock(Mutex); bStop=true; Queue.clear();PriorityTasks.clear(); } Wake.notify_all(); for(FStandaloneWorkerThread& Worker:Workers)if(Worker.joinable())Worker.join(); Workers.clear(); std::lock_guard Lock(Mutex); Ready.clear();Outstanding.clear();Desired.clear();Running.clear(); } FStandaloneStreamingUpdate UpdateDesired( const FVector& CameraPositionVoxels,const std::vector& ResidentKeys, uint64 CrossingIndex=0) { if(bHasLastCameraPosition) { const FVector Delta=CameraPositionVoxels-LastCameraPositionVoxels; const double LengthSquared=static_cast(Delta.X)*Delta.X +static_cast(Delta.Y)*Delta.Y; if(LengthSquared>1.0e-12) { const double InverseLength=1.0/std::sqrt(LengthSquared); TravelDirection=FVector(static_cast(Delta.X*InverseLength), static_cast(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) { const auto CoreTiles=CoreWorld->desiredTiles({CameraPositionVoxels.X,CameraPositionVoxels.Y,CameraPositionVoxels.Z}); NewDesired.Tiles.clear();NewDesired.Tiles.reserve(CoreTiles.size()); for(const auto& Key:CoreTiles) NewDesired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod); } return UpdateDesiredSet(std::move(NewDesired),ResidentKeys,CrossingIndex); } FStandaloneStreamingUpdate UpdateDesiredWithSet( FStandaloneDesiredTileSet NewDesired,const std::vector& ResidentKeys, uint64 CrossingIndex=0) { return UpdateDesiredSet(std::move(NewDesired),ResidentKeys,CrossingIndex); } std::vector TakeReady() { std::vector Out; std::lock_guard Lock(Mutex); Out.swap(Ready); for(const FStandaloneCpuTile& Tile:Out)Outstanding.erase(Tile.Key); return Out; } FStandaloneStreamingUpdate RestrictDesiredTileFilterKeepingStarted( const std::vector& BaseFilter, const std::vector& CandidateKeys, const std::vector& ResidentKeys, std::vector& RetainedCandidateKeys) { std::unordered_set Candidates; Candidates.reserve(CandidateKeys.size()*2+1); for(const FVoxelTileKey& Key:CandidateKeys)Candidates.insert(Key); FStandaloneStreamingUpdate Stats; std::lock_guard Lock(Mutex); std::unordered_set Retained; Retained.reserve(CandidateKeys.size()*2+1); for(const FVoxelTileKey& Key:Running) if(Candidates.contains(Key))Retained.insert(Key); for(const FStandaloneCpuTile& Tile:Ready) if(Candidates.contains(Tile.Key))Retained.insert(Tile.Key); for(const FVoxelTileKey& Key:ResidentKeys) if(Candidates.contains(Key))Retained.insert(Key); TileFilter.clear(); TileFilter.reserve((BaseFilter.size()+Retained.size())*2+1); for(const FVoxelTileKey& Key:BaseFilter)TileFilter.insert(Key); for(const FVoxelTileKey& Key:Retained)TileFilter.insert(Key); bUseTileFilter=true; FStandaloneDesiredTileSet NewDesired=CurrentDesired; NewDesired.Tiles.erase(std::remove_if(NewDesired.Tiles.begin(),NewDesired.Tiles.end(), [&](const FVoxelTileKey& Key){return !TileFilter.contains(Key);}),NewDesired.Tiles.end()); Stats.CenterChunk=NewDesired.CenterChunk; Stats.DesiredTiles=static_cast(NewDesired.Tiles.size()); std::unordered_set ResidentSet; ResidentSet.reserve(ResidentKeys.size()*2+1); for(const FVoxelTileKey& Key:ResidentKeys)ResidentSet.insert(Key); for(const FVoxelTileKey& Key:NewDesired.Tiles) { if(Key.LevelFullResClipLevels)++Stats.FullResTiles; if(ResidentSet.contains(Key))++Stats.RetainedResidentTiles; } Desired.clear();Desired.reserve(NewDesired.Tiles.size()*2+1); for(const FVoxelTileKey& Key:NewDesired.Tiles)Desired.insert(Key); FullResTileCount=Stats.FullResTiles; std::unordered_map CancelledByCrossing; auto QueueEnd=std::remove_if(Queue.begin(),Queue.end(),[&](const FStandaloneTileJob& Job) { if(Desired.contains(Job.Key))return false; Outstanding.erase(Job.Key);++Stats.CancelledQueuedJobs; ++CancelledByCrossing[Job.RequestedCrossing];return true; }); Queue.erase(QueueEnd,Queue.end()); std::make_heap(Queue.begin(),Queue.end(),FStandaloneTileJobHeapOrder{}); Stats.CancelledByCrossing.reserve(CancelledByCrossing.size()); for(const auto& Pair:CancelledByCrossing)Stats.CancelledByCrossing.push_back(Pair); CurrentDesired=std::move(NewDesired); RetainedCandidateKeys.assign(Retained.begin(),Retained.end()); std::sort(RetainedCandidateKeys.begin(),RetainedCandidateKeys.end(), [](const FVoxelTileKey& A,const FVoxelTileKey& B) { if(A.Level!=B.Level)return A.Level Lock(Mutex);return Desired.contains(Key);} int32 DesiredCount()const {std::lock_guard Lock(Mutex);return static_cast(Desired.size());} int32 FullResDesiredCount()const {std::lock_guard Lock(Mutex);return FullResTileCount;} FIntVector CenterChunk()const {std::lock_guard Lock(Mutex);return CurrentDesired.CenterChunk;} int32 GetWorkerCount()const{return WorkerCount;} uint64 GetCacheHits()const{return CacheHitCount.load(std::memory_order_relaxed);} void SetMesherStageProfilingEnabled(bool Enabled) {bProfileMesherStages=Enabled;} void SetTileDrainInstrumentationEnabled(bool Enabled) {bInstrumentTileDrain=Enabled;} void SetPassageComposerProfilingEnabled(bool Enabled) {bProfilePassageComposer=Enabled;} void SetExactMidpointDensityMemoEnabled(bool Enabled) {bExactMidpointDensityMemo=Enabled;} void SetActiveLaneBatchingEnabled(bool Enabled) {bActiveLaneBatching=Enabled;} void SetProofBlockCells(int32 Cells) {ProofBlockCells=std::clamp(Cells,1,32);} void SetUniformBlockProofAuditEnabled(bool Enabled) {bAuditUniformBlockProofs=Enabled;} FStandaloneCpuTile GenerateDiagnostic(const FVoxelTileKey& Key, std::uint32_t GlobalOctaveDrop=0,std::uint32_t CellStride=1, bool ForceGenerate=false) { FStandaloneTileJob Job;Job.Key=Key;Job.StartedAt=FClock::now();Job.QueuedAt=Job.StartedAt; return Generate(Job,GlobalOctaveDrop,CellStride,ForceGenerate); } private: FStandaloneStreamingUpdate UpdateDesiredSet(FStandaloneDesiredTileSet NewDesired, const std::vector& ResidentKeys,uint64 CrossingIndex) { if(MaxLevelFilter>=0) NewDesired.Tiles.erase(std::remove_if(NewDesired.Tiles.begin(),NewDesired.Tiles.end(), [&](const FVoxelTileKey& Key){return Key.Level>MaxLevelFilter;}),NewDesired.Tiles.end()); if(bUseTileFilter) NewDesired.Tiles.erase(std::remove_if(NewDesired.Tiles.begin(),NewDesired.Tiles.end(), [&](const FVoxelTileKey& Key){return !TileFilter.contains(Key);}),NewDesired.Tiles.end()); FStandaloneStreamingUpdate Stats; Stats.CenterChunk=NewDesired.CenterChunk; Stats.DesiredTiles=static_cast(NewDesired.Tiles.size()); std::unordered_set Resident; Resident.reserve(ResidentKeys.size()*2+1); for(const FVoxelTileKey& Key:ResidentKeys)Resident.insert(Key); for(const FVoxelTileKey& Key:NewDesired.Tiles) { if(Key.LevelFullResClipLevels)++Stats.FullResTiles; if(Resident.contains(Key))++Stats.RetainedResidentTiles; } std::lock_guard Lock(Mutex); Desired.clear();Desired.reserve(NewDesired.Tiles.size()*2+1); for(const FVoxelTileKey& Key:NewDesired.Tiles)Desired.insert(Key); FullResTileCount=Stats.FullResTiles; std::unordered_map CancelledByCrossing; auto QueueEnd=std::remove_if(Queue.begin(),Queue.end(),[&](const FStandaloneTileJob& Job) { if(Desired.contains(Job.Key))return false; Outstanding.erase(Job.Key);++Stats.CancelledQueuedJobs; ++CancelledByCrossing[Job.RequestedCrossing];return true; }); Queue.erase(QueueEnd,Queue.end()); Stats.CancelledByCrossing.reserve(CancelledByCrossing.size()); for(const auto& Pair:CancelledByCrossing)Stats.CancelledByCrossing.push_back(Pair); std::vector 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(1<(Key.Coord.X)+0.5)*Scale-NewDesired.CenterChunk.X; const double OffsetY=(static_cast(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 TierAB.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 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 PriorityRanks; PriorityRanks.reserve(Prioritized.size()*2+1); for(size_t Rank=0;RankStrateContentCutMinLevel<9&&NewDesired.bHasStrate ? NewDesired.StrateBottomChunkZ*CHUNK_SIZE:MIN_int32; const int32 BandMax=World->StrateContentCutMinLevel<9&&NewDesired.bHasStrate ? (NewDesired.StrateTopChunkZ+1)*CHUNK_SIZE:MAX_int32; for(const FVoxelTileKey& Key:NewDesired.Tiles) { if(Resident.contains(Key)||Outstanding.contains(Key))continue; FStandaloneTileJob Job; Job.Key=Key;Job.BandZMin=BandMin;Job.BandZMax=BandMax; Job.RequestedCrossing=CrossingIndex;Job.PriorityRank=PriorityRanks.at(Key); Job.QueuedAt=FClock::now(); Queue.push_back(Job); if(bColdTimelineTrace) RecordTimelineEvent(FStandaloneCpuTileTimelineEvent::EType::Queued,Key,-1, Queue.size(),Running.size(),Job.PriorityRank,Job.QueuedAt,Job.QueuedAt); Outstanding.insert(Key);++Stats.NewJobs; } CurrentDesired=std::move(NewDesired); std::make_heap(Queue.begin(),Queue.end(),FStandaloneTileJobHeapOrder{}); Wake.notify_all(); return Stats; } FStandaloneCpuTile Generate(const FStandaloneTileJob& Job, std::uint32_t DiagnosticGlobalOctaveDrop=0,std::uint32_t DiagnosticCellStride=1, bool ForceGenerate=false) { const auto StartTime=Job.StartedAt; FStandaloneCpuTile Out;Out.Key=Job.Key;Out.RequestedCrossing=Job.RequestedCrossing; Out.QueuedAt=Job.QueuedAt;Out.StartedAt=Job.StartedAt; if(MeshCache&&!ForceGenerate) { if(const FStandaloneCpuTile* Cached=MeshCache->Find(Job.Key)) { Out=*Cached;Out.RequestedCrossing=Job.RequestedCrossing; Out.WorkerSeconds=std::chrono::duration(FClock::now()-StartTime).count(); Out.CompletedAt=FClock::now(); if(bHashDensity) {Out.MeshHash=HashStandaloneCpuTileMesh(Out);Out.bHasMeshHash=true;} CacheHitCount.fetch_add(1,std::memory_order_relaxed); return Out; } } UVoxelMarchingCubesMesher Mesher; Mesher.SetGenerator(World->Generator.get()); Mesher.bStandaloneSkipUvAndColors=bSkipUvAndColors; Mesher.bStandaloneSkipUV=bSkipUV; Mesher.bStandaloneSkipColors=bSkipColors; Mesher.bStandaloneSkipMaterialGroups=bSkipMaterialGroups; Mesher.bGenerateSkirts=World->bGenerateSkirts; Mesher.SkirtCells=static_cast(World->SkirtCells); Mesher.LODOctaveDrop=World->LODOctaveDrop; const int32 Level=Job.Key.Level; const int32 Extent=Job.Key.ExtentVoxels(); const int32 Cells=LevelFullResClipLevels?CHUNK_SIZE:FMath::Max(1,World->CoarseTileCells); const int32 Step=FMath::Max(1,Extent/Cells); int64 SampleCount=0; const bool bProfile=VoxelDensityProfile::GetMode()!=VoxelDensityProfile::EMode::Disabled; const VoxelDensityProfile::FSnapshot Before=bProfile ? VoxelDensityProfile::SnapshotCurrentThread():VoxelDensityProfile::FSnapshot{}; if(CoreWorld) { VoxelForgeCore::TileGenerationOptions Options; Options.bandMinZVoxel=Job.BandZMin;Options.bandMaxZVoxel=Job.BandZMax; Options.hashDensity=bHashDensity; Options.skipUvAndColors=bSkipUvAndColors; Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors; Options.captureColumnCoordinates=bCaptureColumnCoordinates; Options.useSharedColumnCache=bSharedColumnCache; Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels; Options.profileMesherStages=(bProfileMesherStages||bInstrumentTileDrain)&&!ForceGenerate; Options.profilePassageComposer=bProfilePassageComposer; Options.instrumentTileDrain=bInstrumentTileDrain&&!ForceGenerate; Options.exactMidpointDensityMemo=bExactMidpointDensityMemo&&!ForceGenerate; Options.activeLaneBatching=bActiveLaneBatching&&!ForceGenerate; Options.proofBlockCells=static_cast(ProofBlockCells); Options.auditUniformBlockProofs=bAuditUniformBlockProofs&&!ForceGenerate; Options.diagnosticGlobalOctaveDrop=DiagnosticGlobalOctaveDrop; Options.diagnosticCellStride=std::max(1,DiagnosticCellStride); Options.diagnosticColumnInterpolationStride= std::max(1,DiagnosticCellStride); std::string Error; auto Result=CoreWorld->generateTile( {{Job.Key.Coord.X,Job.Key.Coord.Y,Job.Key.Coord.Z},Job.Key.Level},Options,Error); if(!Result) { std::fprintf(stderr,"[VoxelForgeCore] tile generation failed: %s\n",Error.c_str()); Out.bGenerationFailed=true; Out.WorkerSeconds=std::chrono::duration(FClock::now()-StartTime).count(); Out.CompletedAt=FClock::now();return Out; } Out.DensitySamples=Result->densitySampleCount; Out.CoreDiagnostics=Result->diagnostics; Out.SurfaceVertexCount=Result->groups.surfaceVertexCount; Out.SkirtTriangleCount=Result->groups.skirtTriangleCount; Out.MeshPayloadAllocatedBytes=Result->meshPayloadAllocatedBytes; Out.DensityHash=Result->densityHash;Out.MeshHash=Result->meshHash; Out.PayloadHash=Result->payloadHash;Out.TopologyHash=Result->topologyHash; Out.MaterialHash=Result->materialHash; Out.bHasDensityHash=Result->hasDensityHash;Out.bHasMeshHash=Result->hasMeshHash; Out.bHasPayloadHash=Result->hasPayloadHash;Out.bHasTopologyHash=Result->hasTopologyHash; Out.bHasMaterialHash=Result->hasMaterialHash; Out.Vertices.reserve(Result->vertices.size()); for(const auto& Vertex:Result->vertices) Out.Vertices.push_back({ {static_cast(Vertex.position.x*0.01),static_cast(Vertex.position.y*0.01), static_cast(Vertex.position.z*0.01)}, {static_cast(Vertex.normal.x),static_cast(Vertex.normal.y), static_cast(Vertex.normal.z)}, Vertex.hostMaterialId,Vertex.resourceMaterialIds,Vertex.resourceWeightsQ16}); Out.Indices=std::move(Result->indices); Out.TriangleSkirtFaceMasks=std::move(Result->skirtFaceMasks); if(bProfile) { const VoxelDensityProfile::FSnapshot After=VoxelDensityProfile::SnapshotCurrentThread(); for(size_t I=0;I=Before.Cycles[I]?After.Cycles[I]-Before.Cycles[I]:0; } Out.WorkerSeconds=std::chrono::duration(FClock::now()-StartTime).count(); Out.CompletedAt=FClock::now();return Out; } std::vector DensityValues; FVoxelMeshData Mesh=Mesher.GenerateMesh(Job.Key.OriginVoxels(),Step,Cells,nullptr, Job.BandZMin,Job.BandZMax,&SampleCount,nullptr,nullptr, bHashDensity?&DensityValues:nullptr); if(bHashDensity) { Out.bHasDensityHash=true; for(const float Density:DensityValues) { Out.DensityHash^=std::bit_cast(Density); Out.DensityHash*=1099511628211ull; } } if(bProfile) { const VoxelDensityProfile::FSnapshot After=VoxelDensityProfile::SnapshotCurrentThread(); for(size_t I=0;I=Before.Cycles[Bucket] ?After.Cycles[Bucket]-Before.Cycles[Bucket]:0; } } Out.DensitySamples=static_cast(FMath::Max(0,SampleCount)); Out.SurfaceVertexCount=static_cast(std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num())); Out.SkirtTriangleCount=static_cast(std::max(0,Mesh.NumSkirtTriangles)); Out.MeshPayloadAllocatedBytes=static_cast(Mesh.Vertices.GetAllocatedSize()) +static_cast(Mesh.Normals.GetAllocatedSize()) +static_cast(Mesh.UVs.GetAllocatedSize()) +static_cast(Mesh.Colors.GetAllocatedSize()) +static_cast(Mesh.TriangleSkirtFaceMasks.GetAllocatedSize()) +static_cast(Mesh.Triangles.GetAllocatedSize()); if(bHashDensity) { Out.PayloadHash=HashStandaloneMeshPayload(Mesh);Out.bHasPayloadHash=true; Out.TopologyHash=HashStandaloneMeshTopology(Mesh);Out.bHasTopologyHash=true; } Out.Vertices.reserve(static_cast(Mesh.Vertices.Num())); for(int32 I=0;I(P.X*0.01),static_cast(P.Y*0.01),static_cast(P.Z*0.01)}, {static_cast(N.X),static_cast(N.Y),static_cast(N.Z)}}); } Out.Indices.reserve(static_cast(Mesh.Triangles.Num())); for(const int32 Index:Mesh.Triangles)Out.Indices.push_back(static_cast(Index)); Out.TriangleSkirtFaceMasks.assign(Mesh.TriangleSkirtFaceMasks.begin(), Mesh.TriangleSkirtFaceMasks.end()); Out.TriangleSkirtFaceMasks.resize(Out.Indices.size()/3,0); if(bHashDensity) {Out.MeshHash=HashStandaloneCpuTileMesh(Out);Out.bHasMeshHash=true;} Out.WorkerSeconds=std::chrono::duration(FClock::now()-StartTime).count(); Out.CompletedAt=FClock::now(); return Out; } void WorkerLoop(int32 WorkerId) { constexpr uint32 PriorityTaskBurst=3; uint32 ConsecutivePriorityTasks=0; for(;;) { FStandaloneTileJob Job; std::function PriorityTask; size_t DispatchQueueDepth=0,DispatchActiveWorkers=0; { std::unique_lock Lock(Mutex); Wake.wait(Lock,[&](){return bStop||!PriorityTasks.empty()||!Queue.empty();}); if(bStop)return; const bool bPreferPriority=!PriorityTasks.empty() &&(Queue.empty()||ConsecutivePriorityTasks Lock(Mutex); if(bStop)return; Running.erase(Tile.Key); if(bColdTimelineTrace) RecordTimelineEvent(FStandaloneCpuTileTimelineEvent::EType::Finish,Tile.Key, WorkerId,Queue.size(),Running.size(),Job.PriorityRank,Tile.CompletedAt, Tile.QueuedAt,Tile.StartedAt,Tile.CompletedAt,Tile.WorkerSeconds*1000.0, &Tile.CoreDiagnostics,Tile.DensitySamples,Tile.SkirtTriangleCount); Ready.push_back(std::move(Tile)); } } } using FClock=std::chrono::steady_clock; double TimelineMilliseconds(FClock::time_point At)const { return At.time_since_epoch().count()==0?-1.0: std::chrono::duration(At-TimelineOrigin).count(); } uint64 TimelineFrame()const { return TimelineFrameOrdinal?TimelineFrameOrdinal->load(std::memory_order_relaxed):0; } static void PrintTileStageEvent(const FStandaloneCpuTileTimelineEvent& Event) { const auto& D=Event.Diagnostics; std::printf("[CpuTileStages] key=(%d,%d,%d,lod%d) worker_id=%d worker_ms=%.3f 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", Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level, Event.WorkerId,Event.WorkerMilliseconds, static_cast(Event.DensitySamples),D.classificationMilliseconds, D.columnComputeMilliseconds,D.columnEvaluationExclusiveMilliseconds, D.marchingCubesExclusiveMilliseconds+D.marchingCubesSkirtMilliseconds, D.marchingCubesDensityGridMilliseconds,D.marchingCubesCellClassificationMilliseconds, D.marchingCubesGradientNormalsMilliseconds,D.marchingCubesEdgeInterpolationMilliseconds, D.marchingCubesVertexDedupStreamMilliseconds,D.marchingCubesSkirtMilliseconds, D.marchingCubesOtherMilliseconds,D.farConnectorShellMilliseconds, D.groundRefinementExclusiveMilliseconds,D.capRefinementExclusiveMilliseconds, D.farHorizonMilliseconds,D.meshAssemblyMilliseconds, static_cast(D.marchingCubesCandidateCells), static_cast(D.marchingCubesCellClassificationCalls), static_cast(D.marchingCubesSurfaceCellCalls), static_cast(D.marchingCubesProofSkippedSolidCells), static_cast(D.marchingCubesProofSkippedAirCells), static_cast(D.marchingCubesProofSkippedDensitySamples), static_cast(D.marchingCubesZBandRejectedCells), static_cast(D.marchingCubesX8DensityCalls), static_cast(D.marchingCubesX8ActiveLanes), static_cast(D.marchingCubesGradientCalls), static_cast(D.marchingCubesGradientMemoHits), static_cast(D.marchingCubesGradientFallbackDensityCalls), static_cast(D.marchingCubesEdgeMidpointDensityCalls), static_cast(D.marchingCubesEdgeMidpointDensityMemoHits), static_cast(D.marchingCubesSkirtEdgeCandidates), static_cast(D.marchingCubesSkirtBoundaryCandidates), static_cast(D.marchingCubesSkirtDensityProbes), Event.SkirtTriangles, static_cast(D.islandLocalCacheHits), static_cast(D.islandLocalCacheMisses), static_cast(D.islandLocalCacheConflictReplacements), static_cast(D.islandSharedCacheHits), static_cast(D.islandSharedCacheMisses), static_cast(D.islandSharedCacheComputes), static_cast(D.islandSharedCacheConflictReplacements), static_cast(D.islandSharedCacheLockWaitNanoseconds)/1000.0); if(D.passageComposerProfiled) std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu native_field_calls=%llu features=%llu route_candidates=%llu mouth_candidates=%llu queries=%llu passage_iterations=%llu upper_mouth_checks=%llu column_candidates=%llu column_distance_calls=%llu negative_bounds=%llu negative_rejects=%llu positive_bounds=%llu positive_rejects=%llu carve_segment_visits=%llu smooth_carve_joins=%llu support_segment_visits=%llu smooth_support_joins=%llu support_capsule_checks=%llu later_bore_checks=%llu other_capsule_checks=%llu mouth_support_calls=%llu mouth_route_segment_checks=%llu relief_field_calls=%llu smoothstep_evaluations=%llu\n", Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level, Event.WorkerMilliseconds,static_cast(Event.DensitySamples), static_cast(D.fieldCalls), static_cast(D.passageTileFeatureCount), static_cast(D.passageTileRouteSegmentCandidates), static_cast(D.passageTileMouthSegmentCandidates), static_cast(D.passageComposerQueries), static_cast(D.passageIterations), static_cast(D.passageUpperMouthFilterChecks), static_cast(D.passageColumnCandidates), static_cast(D.passageColumnDistanceCalls), static_cast(D.passageNegativeBoundChecks), static_cast(D.passageNegativeBoundRejects), static_cast(D.passagePositiveBoundChecks), static_cast(D.passagePositiveBoundRejects), static_cast(D.passageCarveSegmentVisits), static_cast(D.passageSmoothCarveJoins), static_cast(D.passageSupportSegmentVisits), static_cast(D.passageSmoothSupportJoins), static_cast(D.passageSupportCapsuleChecks), static_cast(D.passageLaterBoreChecks), static_cast(D.passageOtherCapsuleChecks), static_cast(D.passageMouthSupportCalls), static_cast(D.passageMouthRouteSegmentChecks), static_cast(D.passageReliefFieldCalls), static_cast(D.passageSmoothstepEvaluations)); } void RecordTimelineEvent(FStandaloneCpuTileTimelineEvent::EType Type, const FVoxelTileKey& Key,int32 WorkerId,size_t QueueDepth,size_t ActiveWorkers, size_t PriorityRank,FClock::time_point At,FClock::time_point Queued={}, FClock::time_point Started={},FClock::time_point Finished={},double WorkerMs=-1.0, const VoxelForgeCore::TileGenerationStats* Diagnostics=nullptr, uint64 DensitySamples=0,uint32 SkirtTriangles=0) { FStandaloneCpuTileTimelineEvent Event; Event.Type=Type;Event.Key=Key;Event.WorkerId=WorkerId; Event.QueueDepth=QueueDepth;Event.ActiveWorkers=ActiveWorkers; Event.PriorityRank=PriorityRank;Event.At=At;Event.QueuedAt=Queued; Event.StartedAt=Started;Event.FinishedAt=Finished;Event.WorkerMilliseconds=WorkerMs; Event.DensitySamples=DensitySamples;Event.SkirtTriangles=SkirtTriangles; if(Diagnostics)Event.Diagnostics=*Diagnostics; if(WorkerId>=0&&static_cast(WorkerId)(WorkerId)].push_back(Event); else TimelineQueueEvents.push_back(Event); } FStandaloneOwnerWorld* World=nullptr; inline static std::atomic bCoarseFirstSchedulingEnabled{true}; FVector LastCameraPositionVoxels=FVector::ZeroVector; FVector TravelDirection=FVector::ZeroVector; bool bHasLastCameraPosition=false; int32 WorkerCount=8; bool bHashDensity=false; const FStandaloneMeshCache* MeshCache=nullptr; int32 MaxLevelFilter=-1; VoxelForgeCore::World* CoreWorld=nullptr; bool bSkipUvAndColors=false; bool bSkipMaterialGroups=false; bool bSkipUV=false; bool bSkipColors=false; bool bCaptureColumnCoordinates=false; bool bSharedColumnCache=true; int32 Lod8HorizonInteriorStepVoxels=0; bool bProfileMesherStages=false; bool bInstrumentTileDrain=false; bool bProfilePassageComposer=false; bool bExactMidpointDensityMemo=false; bool bActiveLaneBatching=false; int32 ProofBlockCells=8; bool bAuditUniformBlockProofs=false; std::atomic CacheHitCount{0}; mutable std::mutex Mutex; std::condition_variable Wake; bool bStop=true; std::vector Workers; std::deque> PriorityTasks; std::vector Queue; std::vector Ready; std::unordered_set Running; std::unordered_set Desired; std::unordered_set Outstanding; std::unordered_set TileFilter; bool bUseTileFilter=false; FStandaloneDesiredTileSet CurrentDesired; int32 FullResTileCount=0; bool bColdTimelineTrace=false; FClock::time_point TimelineOrigin{}; const std::atomic* TimelineFrameOrdinal=nullptr; std::vector TimelineQueueEvents; std::vector> TimelineWorkerEvents; };