#pragma once #include "OwnerWorldLoader.h" #include "StreamingWorld.h" #include #include #include #include #include #include #include #include #include #include #include namespace StandaloneStreamBenchmark { inline void HashWord(uint64& Hash,uint64 Word,int32 Bytes=8) { for(int32 I=0;I(Word>>(I*8)); Hash*=1099511628211ull; } } struct FHashedTile { FVoxelTileKey Key; uint64 Hash=14695981039346656037ull; }; inline bool TileKeyLessForHash(const FVoxelTileKey& A,const FVoxelTileKey& B) { if(A.Level!=B.Level)return A.Level& Rows) { std::sort(Rows.begin(),Rows.end(),[](const FHashedTile& A,const FHashedTile& B) {return TileKeyLessForHash(A.Key,B.Key);}); uint64 Hash=14695981039346656037ull; for(const FHashedTile& Row:Rows) { HashWord(Hash,static_cast(Row.Key.Level),4); HashWord(Hash,static_cast(Row.Key.Coord.X),4); HashWord(Hash,static_cast(Row.Key.Coord.Y),4); HashWord(Hash,static_cast(Row.Key.Coord.Z),4); HashWord(Hash,Row.Hash); } return Hash; } inline int RunPipelinedStreamingBenchmark(std::shared_ptr OwnerWorld, const VoxelForgeCore::ResolvedWorldSnapshot& CoreSnapshot, const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel, VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler, bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors, std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap, int32 FarHorizonLOD8InteriorStepVoxels,bool bReportMeshCoverage=false) { using Clock=std::chrono::steady_clock; FStandaloneOwnerWorld& World=*OwnerWorld; const bool bRoundTunnels=World.Generator->bStandaloneResearchRoundTunnels; const bool bEnhancedTunnelWalls=World.Generator->bStandaloneResearchEnhancedWalls; VoxelForgeCore::World Shape(CoreSnapshot); FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels); const auto CoreTiles=Shape.desiredTiles( {CameraPositionVoxels.X,CameraPositionVoxels.Y,CameraPositionVoxels.Z}); Desired.Tiles.clear();Desired.Tiles.reserve(CoreTiles.size()); for(const auto& Key:CoreTiles) Desired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod); if(MaxLevel>=0) Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(), [&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),Desired.Tiles.end()); if(!Desired.Tiles.empty()) { FIntVector volumeMin(INT32_MAX,INT32_MAX,INT32_MAX); FIntVector volumeMax(INT32_MIN,INT32_MIN,INT32_MIN); int32 minimumLod=INT32_MAX,maximumLod=INT32_MIN; for(const FVoxelTileKey& tile:Desired.Tiles) { const FIntVector origin=tile.OriginVoxels(); const int32 extent=tile.ExtentVoxels(); volumeMin.X=std::min(volumeMin.X,origin.X); volumeMin.Y=std::min(volumeMin.Y,origin.Y); volumeMin.Z=std::min(volumeMin.Z,origin.Z); volumeMax.X=std::max(volumeMax.X,origin.X+extent); volumeMax.Y=std::max(volumeMax.Y,origin.Y+extent); volumeMax.Z=std::max(volumeMax.Z,origin.Z+extent); minimumLod=std::min(minimumLod,tile.Level); maximumLod=std::max(maximumLod,tile.Level); } std::printf("[PipelineVolume] tiles=%zu lod=%d..%d min_voxel=(%d,%d,%d) max_voxel=(%d,%d,%d)\n", Desired.Tiles.size(),minimumLod,maximumLod,volumeMin.X,volumeMin.Y,volumeMin.Z, volumeMax.X,volumeMax.Y,volumeMax.Z); } FIntVector FineMin(INT32_MAX,INT32_MAX,INT32_MAX),FineMax(INT32_MIN,INT32_MIN,INT32_MIN); FIntVector ClipMin(INT32_MAX,INT32_MAX,INT32_MAX),ClipMax(INT32_MIN,INT32_MIN,INT32_MIN); for(const FVoxelTileKey& Tile:Desired.Tiles) { const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels(); ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z); ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent); if(Tile.Level Neighborhood; Neighborhood.reserve(32); for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level==0&&std::abs(Tile.Coord.X-CenterChunk.X)<=1 &&std::abs(Tile.Coord.Y-CenterChunk.Y)<=1&&std::abs(Tile.Coord.Z-CenterChunk.Z)<=1) Neighborhood.insert(Tile); if(Neighborhood.size()!=27) {std::fprintf(stderr,"[StreamPipeline] expected 27 player-neighborhood tiles, found %zu.\n",Neighborhood.size());return 7;} // The initial desired set now owns all clipmap metadata needed by this Core-only run. // Release the separate compatibility-world generation graph before compiling Core. World.Generator.reset(); World.Manager.reset(); World.DefinitionStorage.clear(); World.BiomeStorage.clear(); World.OperationStorage.clear(); World.DensitySamples.clear(); std::string BuildError; const bool bIncludeFarWorld=MaxLevel<0||MaxLevel>=World.FullResClipLevels; const auto ShapeBuildStart=Clock::now(); VoxelForgeCore::ShapeBuildRequest ShapeRequest; ShapeRequest.minVoxel={FineMin.X,FineMin.Y,FineMin.Z}; ShapeRequest.maxVoxel={FineMax.X,FineMax.Y,FineMax.Z}; ShapeRequest.clipMinVoxel={ClipMin.X,ClipMin.Y,ClipMin.Z}; ShapeRequest.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z}; ShapeRequest.workerLimit=static_cast(std::clamp(WorkerCount,1,16)); ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld; ShapeRequest.roundTunnels=bRoundTunnels; ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls; if(!Shape.compile(ShapeRequest,Scheduler,BuildError)) {std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;} const auto ShapeReady=Clock::now(); const double ShapeSeconds=std::chrono::duration(ShapeReady-ShapeBuildStart).count(); FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap, FarHorizonLOD8InteriorStepVoxels); const auto StreamStart=Clock::now(); const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired); if(Update.DesiredTiles!=static_cast(Desired.Tiles.size())) { Streamer.Stop(); std::fprintf(stderr,"[StreamPipeline] expected %zu tiles but queued %d.\n", Desired.Tiles.size(),Update.DesiredTiles);return 7; } std::vector DensityRows,MeshRows,PayloadRows,TopologyRows; int32 Finished=0,NeighborhoodFinished=0,FullDetailFinished=0; int32 FullDetailExpected=0; uint64 WholeNonEmptyMeshTiles=0,WholeTriangleCount=0; struct FColumnXY { std::int64_t X=0,Y=0; bool operator==(const FColumnXY&)const=default; }; struct FColumnXYHash { std::size_t operator()(const FColumnXY& Value)const noexcept { const std::size_t X=std::hash{}(Value.X); const std::size_t Y=std::hash{}(Value.Y); return X^(Y+static_cast(0x9E3779B9u)+(X<<6)+(X>>2)); } }; struct FFirstColumnUse { FVoxelTileKey Tile; std::uint64_t WorkerHash=0; }; std::array,FColumnXYHash>,9> ColumnUsesByLOD; std::array ColumnDuplicateEventsByLOD{},NeighbourDuplicateEventsByLOD{}; std::array CrossWorkerNeighbourDuplicateEventsByLOD{}; std::array ColumnCaptureTilesByLOD{},ExpectedTilesByLOD{}; std::array ColumnCaptureCompleteByLOD{}; ColumnCaptureCompleteByLOD.fill(true); std::array WorkerSecondsByLOD{}; std::array ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{}; std::array GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{}; std::array MeshAssemblyMsByLOD{}; std::array TileCountByLOD{}; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level>=0&&Tile.Level(ExpectedTilesByLOD.size())) ++ExpectedTilesByLOD[static_cast(Tile.Level)]; const auto AreSameLODNeighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B) { if(A.Level!=B.Level)return false; const std::int64_t DX=std::abs(static_cast(A.Coord.X)-B.Coord.X); const std::int64_t DY=std::abs(static_cast(A.Coord.Y)-B.Coord.Y); const std::int64_t DZ=std::abs(static_cast(A.Coord.Z)-B.Coord.Z); return std::max({DX,DY,DZ})<=1&&(DX+DY+DZ)>0; }; uint64 NativeSolidBoxes=0,NativeAirBoxes=0,NativeMixedBoxes=0,NativeUnknownBoxes=0; uint64 NativeFieldCalls=0,NativeColumnHits=0,NativeColumnMisses=0,NativeMeshingSkips=0; double NativeClassificationMilliseconds=0.0,NativeColumnComputeMilliseconds=0.0; double NativeMeshingMilliseconds=0.0; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level Ready=Streamer.TakeReady(); if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;} for(const FStandaloneCpuTile& Tile:Ready) { ++Finished; if(bReportMeshCoverage) { const uint64 TileTriangleCount=static_cast(Tile.Indices.size()/3); WholeTriangleCount+=TileTriangleCount; if(TileTriangleCount>0)++WholeNonEmptyMeshTiles; } if(Tile.Key.Level>=0&&Tile.Key.Level(TileCountByLOD.size())) { const std::size_t LOD=static_cast(Tile.Key.Level); ++TileCountByLOD[LOD]; WorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds; ColumnEvalMsByLOD[LOD]+=Tile.CoreDiagnostics.columnEvaluationExclusiveMilliseconds; MarchingExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.marchingCubesExclusiveMilliseconds; GroundRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.groundRefinementExclusiveMilliseconds; CapRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.capRefinementExclusiveMilliseconds; MeshAssemblyMsByLOD[LOD]+=Tile.CoreDiagnostics.meshAssemblyMilliseconds; } if(bProfileColumnOverlap&&Tile.Key.Level>=0 &&Tile.Key.Level(ColumnCaptureTilesByLOD.size())) { const std::size_t LOD=static_cast(Tile.Key.Level); ++ColumnCaptureTilesByLOD[LOD]; ColumnCaptureCompleteByLOD[LOD]=ColumnCaptureCompleteByLOD[LOD] &&Tile.CoreDiagnostics.columnCoordinateCaptureComplete; std::vector> Coordinates= Tile.CoreDiagnostics.evaluatedColumnCoordinates; std::sort(Coordinates.begin(),Coordinates.end()); Coordinates.erase(std::unique(Coordinates.begin(),Coordinates.end()),Coordinates.end()); for(const auto& Coordinate:Coordinates) { const FColumnXY XY{Coordinate[0],Coordinate[1]}; auto [Uses,Inserted]=ColumnUsesByLOD[LOD].try_emplace(XY); std::vector& PriorUses=Uses->second; if(Inserted) { PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash}); continue; } ++ColumnDuplicateEventsByLOD[LOD]; bool bHasNeighbourDuplicate=false,bHasCrossWorkerNeighbourDuplicate=false; for(const FFirstColumnUse& Prior:PriorUses) { if(!AreSameLODNeighbours(Prior.Tile,Tile.Key))continue; bHasNeighbourDuplicate=true; bHasCrossWorkerNeighbourDuplicate=bHasCrossWorkerNeighbourDuplicate ||Prior.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash; } if(bHasNeighbourDuplicate)++NeighbourDuplicateEventsByLOD[LOD]; if(bHasCrossWorkerNeighbourDuplicate) ++CrossWorkerNeighbourDuplicateEventsByLOD[LOD]; PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash}); } } WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt); if(Neighborhood.contains(Tile.Key)) { ++NeighborhoodFinished; NeighborhoodReady=std::max(NeighborhoodReady,Tile.CompletedAt); } if(Tile.Key.Level(Neighborhood.size())) { std::printf("[PipelineMilestone] neighborhood_ready=%.3fs generated=%d/27\n", std::chrono::duration(NeighborhoodReady-ColdStart).count(),NeighborhoodFinished); bNeighborhoodReported=true; } if(!bFullDetailReported&&FullDetailFinished==FullDetailExpected) { std::printf("[PipelineMilestone] full_detail_ready=%.3fs generated=%d/%d\n", std::chrono::duration(FullDetailReady-ColdStart).count(), FullDetailFinished,FullDetailExpected); bFullDetailReported=true; } } Streamer.Stop(); bool bColumnOverlapCapturePassing=true; if(bProfileColumnOverlap) { for(std::size_t LOD=0;LOD(ColumnCaptureTilesByLOD[LOD]), static_cast(ExpectedTilesByLOD[LOD]), ColumnUsesByLOD[LOD].size(), static_cast(ColumnDuplicateEventsByLOD[LOD]), static_cast(NeighbourDuplicateEventsByLOD[LOD]), static_cast(CrossWorkerNeighbourDuplicateEventsByLOD[LOD]), bLODComplete?1:0); } } for(std::size_t LOD=0;LOD(TileCountByLOD[LOD]),WorkerSecondsByLOD[LOD], ColumnEvalMsByLOD[LOD],MarchingExclusiveMsByLOD[LOD], GroundRefineExclusiveMsByLOD[LOD],CapRefineExclusiveMsByLOD[LOD],MeshAssemblyMsByLOD[LOD]); } const double NeighborhoodSeconds=std::chrono::duration(NeighborhoodReady-ColdStart).count(); const double NeighborhoodMeshSeconds=std::chrono::duration(NeighborhoodReady-ShapeReady).count(); const double FullDetailSeconds=std::chrono::duration(FullDetailReady-ColdStart).count(); const double WholeViewSeconds=std::chrono::duration(WholeViewReady-ColdStart).count(); const double StreamSeconds=std::chrono::duration(WholeViewReady-StreamStart).count(); std::printf("[StreamPipeline] workers=%d shape=%.3fs neighborhood_mesh=%.3fs neighborhood_ready=%.3fs full_detail_ready=%.3fs whole_view_ready=%.3fs neighborhood_tiles=%d/27 full_detail_tiles=%d/%d whole_tiles=%d/%zu stream_wall=%.3fs cache_hits=0 full_payload=1\n", WorkerCount,ShapeSeconds,NeighborhoodMeshSeconds,NeighborhoodSeconds, FullDetailSeconds,WholeViewSeconds,NeighborhoodFinished, FullDetailFinished,FullDetailExpected,Finished,Desired.Tiles.size(),StreamSeconds); if(bReportMeshCoverage) std::printf("[PipelineMeshCoverage] tiles=%d nonempty_mesh_tiles=%llu indexed_triangles=%llu\n", Finished,static_cast(WholeNonEmptyMeshTiles), static_cast(WholeTriangleCount)); std::printf("[WorldAssemblerTilePipeline] full_detail_tiles=%d solid_boxes=%llu air_boxes=%llu mixed_boxes=%llu unknown_boxes=%llu whole_tile_skips=%llu field_calls=%llu column_cache_hits=%llu column_cache_misses=%llu column_compute_worker_ms=%.3f classification_worker_ms=%.3f meshing_worker_ms=%.3f\n", FullDetailExpected,static_cast(NativeSolidBoxes), static_cast(NativeAirBoxes), static_cast(NativeMixedBoxes), static_cast(NativeUnknownBoxes), static_cast(NativeMeshingSkips), static_cast(NativeFieldCalls), static_cast(NativeColumnHits), static_cast(NativeColumnMisses), NativeColumnComputeMilliseconds,NativeClassificationMilliseconds, NativeMeshingMilliseconds); bool bHashesPassing=true; if(bHashDensity) { bHashesPassing=DensityRows.size()==Desired.Tiles.size() &&MeshRows.size()==Desired.Tiles.size() &&PayloadRows.size()==Desired.Tiles.size() &&TopologyRows.size()==Desired.Tiles.size(); std::vector FullDensity,FullMesh,FullPayload,FullTopology; for(const FHashedTile& Row:DensityRows)if(Row.Key.Level(HashTileRows(FullDensity)), static_cast(HashTileRows(FullMesh)), static_cast(HashTileRows(FullPayload)), static_cast(HashTileRows(FullTopology))); std::printf("[PipelineWholeViewHash] tiles=%zu density=0x%016llx mesh=0x%016llx payload=0x%016llx topology=0x%016llx\n", Desired.Tiles.size(),static_cast(HashTileRows(DensityRows)), static_cast(HashTileRows(MeshRows)), static_cast(HashTileRows(PayloadRows)), static_cast(HashTileRows(TopologyRows))); } return NeighborhoodFinished==static_cast(Neighborhood.size()) &&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing &&(!bProfileColumnOverlap||bColumnOverlapCapturePassing)?0:6; } } // namespace StandaloneStreamBenchmark