standalone: cheap far-survival repairs -- whole view 1.80 -> 1.40 s, payload 277 -> 111 MiB
- Far repairs are source-aware (only where the sampled mesh lost a shell) and redundant radius probes trimmed; ~175k two-triangle repair panels remain (+18 MiB over the no-shell 92.6 MiB). - Pipelined start builds the shape world once; the 27-tile neighbourhood is ready ~0.73 s from process start; pipelined whole view is no slower than direct. - Far audit passes, bad-shell control still fails (16 missing / 16 holes); look audits pass; routes 5642/5642; 60 W1/W8 + direct/pipeline hash fields equal; six fixed captures pixel-identical. Verified by me head to head (8 workers, cold, BelowNormal 0xFF00, quiet machine, two runs each): 1.40 s vs 1.80 s for the previous default. 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
a40b0454b3
commit
d4fa89204d
4 files changed
+837
-303
No files matched your search
@@ -674,7 +674,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("Standalone max clip level override applied after world reload: max_level=%d.\n",
|
||||
MaxClipLevelOverride);
|
||||
}
|
||||
if(bShapeWorld&&!BuildShapeWorldForCamera(Error,false,true))return false;
|
||||
// Compile one world field before streaming. The streamer already prioritizes the
|
||||
// player's neighborhood, so a second near-only field would only duplicate work.
|
||||
if(bShapeWorld&&!BuildShapeWorldForCamera(Error,true,false))return false;
|
||||
LoadMeshCache();
|
||||
return true;
|
||||
}
|
||||
@@ -1889,111 +1891,64 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
if(FineMin.X==INT32_MAX||ClipMin.X==INT32_MAX)
|
||||
{std::fprintf(stderr,"[StreamPipeline] no desired detail or clipmap tiles.\n");return 7;}
|
||||
|
||||
const FIntVector CenterChunk=Desired.CenterChunk;
|
||||
std::vector<FVoxelTileKey> Neighborhood;
|
||||
FIntVector NearMin(INT32_MAX,INT32_MAX,INT32_MAX),NearMax(INT32_MIN,INT32_MIN,INT32_MIN);
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> 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)continue;
|
||||
Neighborhood.push_back(Tile);const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels();
|
||||
NearMin.X=std::min(NearMin.X,Origin.X);NearMin.Y=std::min(NearMin.Y,Origin.Y);NearMin.Z=std::min(NearMin.Z,Origin.Z);
|
||||
NearMax.X=std::max(NearMax.X,Origin.X+Extent);NearMax.Y=std::max(NearMax.Y,Origin.Y+Extent);NearMax.Z=std::max(NearMax.Z,Origin.Z+Extent);
|
||||
}
|
||||
if(Neighborhood.empty())
|
||||
{std::fprintf(stderr,"[StreamPipeline] the player neighborhood is absent from the desired set.\n");return 7;}
|
||||
constexpr int32 NearBuildPad=64;
|
||||
NearMin.X-=NearBuildPad;NearMin.Y-=NearBuildPad;NearMin.Z-=NearBuildPad;
|
||||
NearMax.X+=NearBuildPad;NearMax.Y+=NearBuildPad;NearMax.Z+=NearBuildPad;
|
||||
const int32 NearWorkers=WorkerCount>=8?2:1;
|
||||
const int32 FullBuildWorkers=WorkerCount>=8?6:1;
|
||||
auto NearShape=std::make_unique<FStandaloneShapeWorld>();
|
||||
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;}
|
||||
|
||||
FStandaloneShapeWorld Shape;
|
||||
std::string BuildError;
|
||||
const auto NearBuildStart=Clock::now();
|
||||
if(!NearShape->Build(World,NearMin,NearMax,ClipMin,ClipMax,NearWorkers,
|
||||
DetailVariant,false,BuildError))
|
||||
{std::fprintf(stderr,"[StreamPipeline] near shape build failed: %s\n",BuildError.c_str());return 2;}
|
||||
const double NearShapeSeconds=std::chrono::duration<double>(Clock::now()-NearBuildStart).count();
|
||||
FStandaloneShapeWorldAccess::SetActive(NearShape.get());
|
||||
FStandaloneStreamingGenerator NearStreamer(World,NearWorkers,bHashDensity,nullptr,MaxLevel,
|
||||
const bool bIncludeFarWorld=MaxLevel<0||MaxLevel>=World.FullResClipLevels;
|
||||
const auto ShapeBuildStart=Clock::now();
|
||||
if(!Shape.Build(World,FineMin,FineMax,ClipMin,ClipMax,WorkerCount,
|
||||
DetailVariant,bIncludeFarWorld,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<double>(ShapeReady-ShapeBuildStart).count();
|
||||
FStandaloneShapeWorldAccess::SetActive(&Shape);
|
||||
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
NearStreamer.SetDesiredTileFilter(Neighborhood);
|
||||
const auto NearStreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate NearUpdate=NearStreamer.Start(CameraPositionVoxels);
|
||||
if(NearUpdate.DesiredTiles!=static_cast<int32>(Neighborhood.size()))
|
||||
{NearStreamer.Stop();FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
std::fprintf(stderr,"[StreamPipeline] expected %zu player tiles but queued %d.\n",Neighborhood.size(),NearUpdate.DesiredTiles);return 7;}
|
||||
|
||||
FStandaloneShapeWorld FullShape;
|
||||
bool bFullBuildOk=false;std::string FullBuildError;
|
||||
const auto FullBuildStart=Clock::now();
|
||||
std::thread FullBuildThread([&]
|
||||
{bFullBuildOk=FullShape.Build(World,FineMin,FineMax,ClipMin,ClipMax,FullBuildWorkers,
|
||||
DetailVariant,true,FullBuildError);});
|
||||
struct FPipelineTileHashes {uint64 Density=0,Mesh=0,Payload=0,Topology=0;};
|
||||
std::unordered_map<FVoxelTileKey,FPipelineTileHashes,FStandaloneTileKeyHash> NearHashByKey;
|
||||
NearHashByKey.reserve(Neighborhood.size()*2+1);
|
||||
std::vector<FHashedTile> DensityRows,MeshRows,PayloadRows,TopologyRows;
|
||||
std::vector<FStandaloneCpuTile> NearTiles;NearTiles.reserve(Neighborhood.size());
|
||||
std::chrono::steady_clock::time_point NeighborhoodReady{};
|
||||
int32 NearFinished=0;
|
||||
while(NearFinished<NearUpdate.DesiredTiles)
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=NearStreamer.TakeReady();
|
||||
if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;}
|
||||
for(FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++NearFinished;
|
||||
if(bHashDensity)
|
||||
{
|
||||
if(Tile.bHasDensityHash)DensityRows.push_back({Tile.Key,Tile.DensityHash});
|
||||
if(Tile.bHasMeshHash)MeshRows.push_back({Tile.Key,Tile.MeshHash});
|
||||
if(Tile.bHasPayloadHash)PayloadRows.push_back({Tile.Key,Tile.PayloadHash});
|
||||
if(Tile.bHasTopologyHash)TopologyRows.push_back({Tile.Key,Tile.TopologyHash});
|
||||
NearHashByKey[Tile.Key]={Tile.DensityHash,Tile.MeshHash,Tile.PayloadHash,Tile.TopologyHash};
|
||||
}
|
||||
NeighborhoodReady=std::max(NeighborhoodReady,Tile.CompletedAt);
|
||||
NearTiles.push_back(std::move(Tile));
|
||||
}
|
||||
Streamer.Stop();FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
std::fprintf(stderr,"[StreamPipeline] expected %zu tiles but queued %d.\n",
|
||||
Desired.Tiles.size(),Update.DesiredTiles);return 7;
|
||||
}
|
||||
NearStreamer.Stop();
|
||||
const double NearStreamSeconds=std::chrono::duration<double>(NeighborhoodReady-NearStreamStart).count();
|
||||
FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
// Release the small shape before the large build finishes allocating its packed field.
|
||||
NearTiles.clear();NearTiles.shrink_to_fit();NearShape.reset();
|
||||
FullBuildThread.join();
|
||||
const double FullShapeSeconds=std::chrono::duration<double>(Clock::now()-FullBuildStart).count();
|
||||
if(!bFullBuildOk)
|
||||
{std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",FullBuildError.c_str());return 2;}
|
||||
FStandaloneShapeWorldAccess::SetActive(&FullShape);
|
||||
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> NearSet;
|
||||
NearSet.reserve(Neighborhood.size()*2+1);for(const FVoxelTileKey& Key:Neighborhood)NearSet.insert(Key);
|
||||
std::vector<FVoxelTileKey> Remaining;Remaining.reserve(Desired.Tiles.size()-Neighborhood.size());
|
||||
std::vector<FHashedTile> DensityRows,MeshRows,PayloadRows,TopologyRows;
|
||||
int32 Finished=0,NeighborhoodFinished=0,FullDetailFinished=0;
|
||||
int32 FullDetailExpected=0;
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
{
|
||||
if(Tile.Level<World.FullResClipLevels)++FullDetailExpected;
|
||||
if(!NearSet.contains(Tile))Remaining.push_back(Tile);
|
||||
}
|
||||
FStandaloneStreamingGenerator FullStreamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
FullStreamer.SetDesiredTileFilter(Remaining);
|
||||
const auto FillStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate FillUpdate=FullStreamer.Start(CameraPositionVoxels);
|
||||
int32 Finished=0,FullDetailFinished=static_cast<int32>(Neighborhood.size());
|
||||
const int32 FullDetailRemaining=std::max(0,FullDetailExpected-FullDetailFinished);
|
||||
std::chrono::steady_clock::time_point FullDetailReady=NeighborhoodReady,WholeViewReady{};
|
||||
bool bFullDetailReported=false;
|
||||
while(Finished<FillUpdate.DesiredTiles)
|
||||
Clock::time_point NeighborhoodReady{},FullDetailReady{},WholeViewReady{};
|
||||
bool bNeighborhoodReported=false,bFullDetailReported=false;
|
||||
while(Finished<Update.DesiredTiles)
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=FullStreamer.TakeReady();
|
||||
std::vector<FStandaloneCpuTile> Ready=Streamer.TakeReady();
|
||||
if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;}
|
||||
for(const FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++Finished;WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
|
||||
++Finished;
|
||||
WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
|
||||
if(Neighborhood.contains(Tile.Key))
|
||||
{
|
||||
++NeighborhoodFinished;
|
||||
NeighborhoodReady=std::max(NeighborhoodReady,Tile.CompletedAt);
|
||||
}
|
||||
if(Tile.Key.Level<World.FullResClipLevels)
|
||||
{++FullDetailFinished;FullDetailReady=std::max(FullDetailReady,Tile.CompletedAt);}
|
||||
{
|
||||
++FullDetailFinished;
|
||||
FullDetailReady=std::max(FullDetailReady,Tile.CompletedAt);
|
||||
}
|
||||
if(bHashDensity)
|
||||
{
|
||||
if(Tile.bHasDensityHash)DensityRows.push_back({Tile.Key,Tile.DensityHash});
|
||||
@@ -2002,7 +1957,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(Tile.bHasTopologyHash)TopologyRows.push_back({Tile.Key,Tile.TopologyHash});
|
||||
}
|
||||
}
|
||||
if(!bFullDetailReported&&FullDetailRemaining>0&&FullDetailFinished>=FullDetailExpected)
|
||||
if(!bNeighborhoodReported&&NeighborhoodFinished==static_cast<int32>(Neighborhood.size()))
|
||||
{
|
||||
std::printf("[PipelineMilestone] neighborhood_ready=%.3fs generated=%d/27\n",
|
||||
std::chrono::duration<double>(NeighborhoodReady-ColdStart).count(),NeighborhoodFinished);
|
||||
bNeighborhoodReported=true;
|
||||
}
|
||||
if(!bFullDetailReported&&FullDetailFinished==FullDetailExpected)
|
||||
{
|
||||
std::printf("[PipelineMilestone] full_detail_ready=%.3fs generated=%d/%d\n",
|
||||
std::chrono::duration<double>(FullDetailReady-ColdStart).count(),
|
||||
@@ -2010,51 +1971,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
bFullDetailReported=true;
|
||||
}
|
||||
}
|
||||
FullStreamer.Stop();
|
||||
const double WholeViewSeconds=std::chrono::duration<double>(WholeViewReady-ColdStart).count();
|
||||
Streamer.Stop();
|
||||
const double NeighborhoodSeconds=std::chrono::duration<double>(NeighborhoodReady-ColdStart).count();
|
||||
const double NeighborhoodMeshSeconds=std::chrono::duration<double>(NeighborhoodReady-ShapeReady).count();
|
||||
const double FullDetailSeconds=std::chrono::duration<double>(FullDetailReady-ColdStart).count();
|
||||
bool bNearParityPassing=true;
|
||||
if(bHashDensity)
|
||||
{
|
||||
FStandaloneStreamingGenerator NearVerifier(World,NearWorkers,true,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors);
|
||||
NearVerifier.SetDesiredTileFilter(Neighborhood);
|
||||
const FStandaloneStreamingUpdate VerifyUpdate=NearVerifier.Start(CameraPositionVoxels);
|
||||
int32 VerifiedTiles=0,NearHashMismatches=0;
|
||||
while(VerifiedTiles<VerifyUpdate.DesiredTiles)
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=NearVerifier.TakeReady();
|
||||
if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;}
|
||||
for(const FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++VerifiedTiles;
|
||||
const auto It=NearHashByKey.find(Tile.Key);
|
||||
if(It==NearHashByKey.end()||!Tile.bHasDensityHash||!Tile.bHasMeshHash
|
||||
||!Tile.bHasPayloadHash||!Tile.bHasTopologyHash
|
||||
||It->second.Density!=Tile.DensityHash||It->second.Mesh!=Tile.MeshHash
|
||||
||It->second.Payload!=Tile.PayloadHash||It->second.Topology!=Tile.TopologyHash)
|
||||
{
|
||||
++NearHashMismatches;
|
||||
if(NearHashMismatches<=4)
|
||||
std::fprintf(stderr,"[PipelineNeighborhoodHashMismatch] key=(%d,%d,%d,lod%d)\n",
|
||||
Tile.Key.Coord.X,Tile.Key.Coord.Y,Tile.Key.Coord.Z,Tile.Key.Level);
|
||||
}
|
||||
}
|
||||
}
|
||||
NearVerifier.Stop();
|
||||
bNearParityPassing=VerifiedTiles==static_cast<int32>(Neighborhood.size())
|
||||
&&NearHashMismatches==0;
|
||||
std::printf("[PipelineNeighborhoodParity] tiles=%d/%zu hash_fields=%d mismatched_tiles=%d status=%s\n",
|
||||
VerifiedTiles,Neighborhood.size(),VerifiedTiles*4,NearHashMismatches,
|
||||
bNearParityPassing?"PASS":"FAIL");
|
||||
}
|
||||
std::printf("[StreamPipeline] workers=%d near_mesh_workers=%d full_shape_workers=%d near_shape=%.3fs near_mesh=%.3fs full_shape=%.3fs neighborhood_ready=%.3fs full_detail_ready=%.3fs whole_view_ready=%.3fs neighborhood_tiles=%zu/%zu full_detail_tiles=%d/%d whole_tiles=%d/%zu cache_hits=0 full_payload=1\n",
|
||||
WorkerCount,NearWorkers,FullBuildWorkers,NearShapeSeconds,NearStreamSeconds,FullShapeSeconds,
|
||||
NeighborhoodSeconds,FullDetailSeconds,WholeViewSeconds,Neighborhood.size(),Neighborhood.size(),
|
||||
FullDetailFinished,FullDetailExpected,static_cast<int32>(Desired.Tiles.size()),Desired.Tiles.size());
|
||||
const double WholeViewSeconds=std::chrono::duration<double>(WholeViewReady-ColdStart).count();
|
||||
const double StreamSeconds=std::chrono::duration<double>(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);
|
||||
|
||||
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<FHashedTile> FullDensity,FullMesh,FullPayload,FullTopology;
|
||||
for(const FHashedTile& Row:DensityRows)if(Row.Key.Level<World.FullResClipLevels)FullDensity.push_back(Row);
|
||||
for(const FHashedTile& Row:MeshRows)if(Row.Key.Level<World.FullResClipLevels)FullMesh.push_back(Row);
|
||||
@@ -2072,10 +2006,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
static_cast<unsigned long long>(HashTileRows(TopologyRows)));
|
||||
}
|
||||
FStandaloneShapeWorldAccess::SetActive(nullptr);
|
||||
return FullDetailFinished==FullDetailExpected&&Finished==FillUpdate.DesiredTiles
|
||||
&&bNearParityPassing?0:6;
|
||||
return NeighborhoodFinished==static_cast<int32>(Neighborhood.size())
|
||||
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing?0:6;
|
||||
}
|
||||
|
||||
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,const FVector& CameraPositionVoxels,
|
||||
int32 WorkerCount,int32 MaxLevel,bool bRoundTunnels,
|
||||
bool bEnhancedWalls,const FStandaloneMeshCache* Cache,
|
||||
|
||||
Reference in new issue
Block a user